เพิ่มหมายเหตุในปี 2018
จากไป 1.10 มีstrings.Builder
ชนิดโปรดดูที่คำตอบนี้สำหรับรายละเอียดเพิ่มเติม
คำตอบก่อนหน้า 201x
รหัสมาตรฐานของ @ cd1 และคำตอบอื่น ๆ นั้นไม่ถูกต้อง b.N
ไม่ควรตั้งค่าในฟังก์ชั่นมาตรฐาน มันถูกกำหนดโดยเครื่องมือทดสอบ go แบบไดนามิกเพื่อตรวจสอบว่าเวลาดำเนินการทดสอบมีเสถียรภาพ
ฟังก์ชั่นมาตรฐานควรใช้b.N
เวลาทดสอบเดียวกันและการทดสอบภายในลูปควรเหมือนกันสำหรับการวนซ้ำแต่ละครั้ง ดังนั้นฉันจะแก้ไขโดยการเพิ่มวงด้านใน ฉันยังเพิ่มมาตรฐานสำหรับโซลูชันอื่น ๆ :
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)
}
}
สภาพแวดล้อมเป็น OS X 10.11.6, 2.2 GHz Intel Core i7
ผลการทดสอบ:
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
สรุป:
CopyPreAllocate
เป็นวิธีที่เร็วที่สุด; AppendPreAllocate
ค่อนข้างใกล้เคียงกับอันดับ 1 แต่การเขียนรหัสง่ายขึ้น
Concat
มีประสิทธิภาพที่แย่มากทั้งสำหรับความเร็วและการใช้หน่วยความจำ อย่าใช้มัน
Buffer#Write
และBuffer#WriteString
โดยทั่วไปจะมีความเร็วเท่ากันตรงกันข้ามกับสิ่งที่ @ Dani-Br พูดในความคิดเห็น เมื่อพิจารณาstring
อย่างแน่นอน[]byte
ใน Go ก็ทำให้รู้สึก
- bytes.Buffer ใช้วิธีการแก้ปัญหาเช่นเดียว
Copy
กับการรักษาหนังสือพิเศษและสิ่งอื่น ๆ
Copy
และAppend
ใช้ขนาดบูตสแตรป 64 เช่นเดียวกับไบต์ Buffer
Append
ใช้หน่วยความจำและ allocs มากขึ้นฉันคิดว่ามันเกี่ยวข้องกับอัลกอริธึมการเติบโตที่ใช้ มันไม่ได้เพิ่มหน่วยความจำเร็วเท่าไบต์
คำแนะนำ:
- สำหรับงานง่ายๆเช่นสิ่งที่ OP ต้องการฉันจะใช้
Append
หรือAppendPreAllocate
หรือมันเร็วพอและใช้งานง่าย
- หากจำเป็นต้องอ่านและเขียนบัฟเฟอร์พร้อมกันให้ใช้
bytes.Buffer
แน่นอน นั่นคือสิ่งที่มันถูกออกแบบมาสำหรับ