Lưu ý thêm vào năm 2018
Từ Go 1.10 có một strings.Builder
loại, vui lòng xem câu trả lời này để biết thêm chi tiết .
Trả lời trước 201x
Mã điểm chuẩn của @ cd1 và các câu trả lời khác là sai. b.N
không được thiết lập trong chức năng điểm chuẩn. Nó được thiết lập bởi công cụ kiểm tra cờ vây để xác định xem thời gian thực hiện kiểm tra có ổn định không.
Một hàm điểm chuẩn sẽ chạy cùng b.N
thời gian kiểm tra và kiểm tra bên trong vòng lặp sẽ giống nhau cho mỗi lần lặp. Vì vậy, tôi sửa nó bằng cách thêm một vòng lặp bên trong. Tôi cũng thêm điểm chuẩn cho một số giải pháp khác:
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)
}
}
Môi trường là OS X 10.11.6, Intel Core i7 2.2 GHz
Kết quả kiểm tra:
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
Phần kết luận:
CopyPreAllocate
là cách nhanh nhất; AppendPreAllocate
khá gần với số 1, nhưng việc viết mã dễ dàng hơn.
Concat
có hiệu suất thực sự xấu cả về tốc độ và sử dụng bộ nhớ. Đừng sử dụng nó.
Buffer#Write
và Buffer#WriteString
về cơ bản là giống nhau về tốc độ, trái với những gì @ Dani-Br đã nói trong bình luận. Xem xét string
thực sự là []byte
trong Go, nó có ý nghĩa.
- byte.Buffer về cơ bản sử dụng cùng một giải pháp như
Copy
với việc giữ thêm sách và các công cụ khác.
Copy
và Append
sử dụng kích thước bootstrap là 64, giống như byte.Buffer
Append
sử dụng nhiều bộ nhớ và allocs hơn, tôi nghĩ nó liên quan đến thuật toán phát triển mà nó sử dụng. Nó không tăng bộ nhớ nhanh như byte.Buffer
Gợi ý:
- Đối với tác vụ đơn giản như những gì OP muốn, tôi sẽ sử dụng
Append
hoặcAppendPreAllocate
. Nó đủ nhanh và dễ sử dụng.
- Nếu cần đọc và ghi bộ đệm cùng một lúc, sử dụng
bytes.Buffer
tất nhiên. Đó là những gì nó được thiết kế cho.