2018'de not eklendi
Go 1.10'dan bir strings.Builder
tür var, lütfen daha fazla ayrıntı için bu cevaba bir göz atın .
201x öncesi cevap
@ Cd1 ve diğer yanıtların karşılaştırma kodu yanlış. b.N
karşılaştırmalı değerlendirme işlevinde ayarlanmamış olması gerekir. Testin yürütme süresinin kararlı olup olmadığını belirlemek için go test aracı tarafından dinamik olarak ayarlanır.
Bir kıyaslama işlevi aynı test b.N
sürelerini çalıştırmalı ve döngü içindeki test her yineleme için aynı olmalıdır. Bu yüzden bir iç döngü ekleyerek düzeltirim. Ayrıca diğer bazı çözümler için kıyaslamalar da ekliyorum:
package main
import (
"bytes"
"strings"
"testing"
)
const (
sss = "xfoasneobfasieongasbg"
cnt = 10000
)
var (
bbb = []byte(sss)
expected = strings.Repeat(sss, cnt)
)
func BenchmarkCopyPreAllocate(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
bs := make([]byte, cnt*len(sss))
bl := 0
for i := 0; i < cnt; i++ {
bl += copy(bs[bl:], sss)
}
result = string(bs)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkAppendPreAllocate(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, cnt*len(sss))
for i := 0; i < cnt; i++ {
data = append(data, sss...)
}
result = string(data)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkBufferPreAllocate(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
buf := bytes.NewBuffer(make([]byte, 0, cnt*len(sss)))
for i := 0; i < cnt; i++ {
buf.WriteString(sss)
}
result = buf.String()
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkCopy(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
for i := 0; i < cnt; i++ {
off := len(data)
if off+len(sss) > cap(data) {
temp := make([]byte, 2*cap(data)+len(sss))
copy(temp, data)
data = temp
}
data = data[0 : off+len(sss)]
copy(data[off:], sss)
}
result = string(data)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkAppend(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, 64)
for i := 0; i < cnt; i++ {
data = append(data, sss...)
}
result = string(data)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkBufferWrite(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
var buf bytes.Buffer
for i := 0; i < cnt; i++ {
buf.Write(bbb)
}
result = buf.String()
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkBufferWriteString(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
var buf bytes.Buffer
for i := 0; i < cnt; i++ {
buf.WriteString(sss)
}
result = buf.String()
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkConcat(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
var str string
for i := 0; i < cnt; i++ {
str += sss
}
result = str
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
Ortam OS X 10.11.6, 2.2 GHz Intel Core i7'dir
Test sonuçları:
BenchmarkCopyPreAllocate-8 20000 84208 ns/op 425984 B/op 2 allocs/op
BenchmarkAppendPreAllocate-8 10000 102859 ns/op 425984 B/op 2 allocs/op
BenchmarkBufferPreAllocate-8 10000 166407 ns/op 426096 B/op 3 allocs/op
BenchmarkCopy-8 10000 160923 ns/op 933152 B/op 13 allocs/op
BenchmarkAppend-8 10000 175508 ns/op 1332096 B/op 24 allocs/op
BenchmarkBufferWrite-8 10000 239886 ns/op 933266 B/op 14 allocs/op
BenchmarkBufferWriteString-8 10000 236432 ns/op 933266 B/op 14 allocs/op
BenchmarkConcat-8 10 105603419 ns/op 1086685168 B/op 10000 allocs/op
Sonuç:
CopyPreAllocate
en hızlı yoldur; AppendPreAllocate
No.1'e oldukça yakın, ancak kodu yazmak daha kolay.
Concat
hem hız hem de bellek kullanımı için gerçekten kötü bir performansa sahip. Kullanma.
Buffer#Write
ve Buffer#WriteString
@ Dani-Br'nin yorumda söylediklerinin aksine, temel olarak hızda aynı. Düşünüldüğünde string
gerçekten de []byte
mantıklı, Go.
- Tampon temel olarak
Copy
ekstra kitap tutma ve diğer şeylerle aynı çözümü kullanır .
Copy
ve Append
baytlarla aynı 64 olan bir bootstrap boyutu kullanın.
Append
daha fazla bellek ve alaşım kullanın, bence kullandığımız büyüme algoritmasıyla ilgili. Belleği bayt kadar hızlı büyütmüyor.
Öneri:
- OP'nin istediği gibi basit görevler için,
Append
ya da kullanacağım AppendPreAllocate
. Yeterince hızlı ve kullanımı kolaydır.
- Aynı anda tamponu okumak ve yazmak gerekirse
bytes.Buffer
, elbette kullanın . Bunun için tasarlandı.