在需要向一个字节切片添加未知数量的数据的情况下,比如在一个循环中,我既可以使用内建函数
append()
,也可以创建一个新的Buffer
并使用Write()
函数。哪种方法更快?append()
,也可以创建一个新的Buffer
并使用Write()
函数。哪种方法更快?bytes.Buffer
比append
更快(样本:1、2、3、4)。buf.Write(make([]byte, 16))
需要4.6482659秒
,
使用buf = append(buf, make([]byte, 16)...)
需要6.6623811秒
。buf = append(buf, byte(i))
需要445.0255毫秒
,
使用buf.WriteByte(byte(i))
需要1.4410824秒
。bytes.Buffer
使用内置函数copy
,速度很快。
// Write appends the contents of p to the buffer, growing the buffer as
// needed. The return value n is the length of p; err is always nil. If the
// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) { b.lastRead = opInvalid m := b.grow(len(p)) return copy(b.buf[m:], p), nil }
bytes.Buffer
花费了4.8892797秒,而append
花费了7.7514434秒。
请查看以下基准测试结果:
1- 使用append
:
package main
import (
"fmt"
"time"
)
func main() {
buf := []byte{}
data := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
t := time.Now()
for i := 0; i < 100000000; i++ {
buf = append(buf, data...)
}
fmt.Println(time.Since(t))
fmt.Println(len(buf))
}
输出:
7.7514434s
1600000000
2- 使用 bytes.Buffer
package main
import (
"bytes"
"fmt"
"time"
)
func main() {
buf := &bytes.Buffer{}
data := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
t := time.Now()
for i := 0; i < 100000000; i++ {
buf.Write(data)
}
fmt.Println(time.Since(t))
fmt.Println(buf.Len())
}
输出:
4.8892797s
1600000000
3- 使用 bytes.Buffer
和 make([]byte, 16)
:
package main
import (
"bytes"
"fmt"
"time"
)
func main() {
buf := &bytes.Buffer{}
t := time.Now()
for i := 0; i < 100000000; i++ {
buf.Write(make([]byte, 16))
}
fmt.Println(time.Since(t)) // 4.6482659s
fmt.Println(buf.Len()) //1600000000
}
4- 使用 make([]byte, 16)
来配合 append
:
package main
import (
"fmt"
"time"
)
func main() {
buf := []byte{}
t := time.Now()
for i := 0; i < 100000000; i++ {
buf = append(buf, make([]byte, 16)...)
}
fmt.Println(time.Since(t)) // 6.6623811s
fmt.Println(len(buf)) // 1600000000
}
5- 使用 buf = append(buf, byte(i))
耗时 445.0255ms
:
package main
import (
"fmt"
"time"
)
func main() {
buf := []byte{}
t := time.Now()
for i := 0; i < 100000000; i++ {
buf = append(buf, byte(i))
}
fmt.Println(time.Since(t)) // 445.0255ms
fmt.Println(len(buf)) // 100000000
}
6- 使用 buf.WriteByte(byte(i))
耗时 1.4410824s
:
package main
import (
"bytes"
"fmt"
"time"
)
func main() {
buf := &bytes.Buffer{}
t := time.Now()
for i := 0; i < 100000000; i++ {
buf.WriteByte(byte(i))
}
fmt.Println(time.Since(t)) // 1.4410824s
fmt.Println(buf.Len()) // 100000000
}
请参见:
为什么向切片追加元素会影响性能?
append() 的实现在哪里?
如何高效地向可变长度的字符串容器中追加元素(Golang)
randomSlice
,请尝试使用return make([]byte, rand.Intn(1<<10))
而不是return make([]byte, 0, rand.Intn(1<<10))
。使用长度为零的切片不是一个好的基准。 - user6169399append
更快,如下基准测试所示:package x
import (
"bytes"
"math/rand"
"testing"
"time"
)
var startSeed = time.Now().UnixNano()
func randomSlice() []byte {
return make([]byte, 0, rand.Intn(1<<10))
}
func BenchmarkAppend(b *testing.B) {
rand.Seed(startSeed)
b.ResetTimer()
var all []byte
for i := 0; i < b.N; i++ {
all = append(all, randomSlice()...)
}
}
func BenchmarkBufferWrite(b *testing.B) {
rand.Seed(startSeed)
b.ResetTimer()
var buff bytes.Buffer
for i := 0; i < b.N; i++ {
buff.Write(randomSlice())
}
all := buff.Bytes()
_ = all
}
结果:
BenchmarkAppend-4 10000000 206 ns/op 540 B/op 0 allocs/op
BenchmarkBufferWrite-4 10000000 214 ns/op 540 B/op 0 allocs/op