在Go语言中,常见的字符串拼接方式有以下6种:
本文主要是针对上面各种方式进行基准测试,帮助我们在以后的编码过程中选择最优的方式。
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randomString(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func plusConcat(n int, str string) string {
s := ""
for i := 0; i < n; i++ {
s += str
}
return s
}
func sprintfConcat(n int, str string) string {
s := ""
for i := 0; i < n; i++ {
s = fmt.Sprintf("%s%s", s, str)
}
return s
}
func builderConcat(n int, str string) string {
var builder strings.Builder
for i := 0; i < n; i++ {
builder.WriteString(str)
}
return builder.String()
}
func bufferConcat(n int, s string) string {
buf := new(bytes.Buffer)
for i := 0; i < n; i++ {
buf.WriteString(s)
}
return buf.String()
}
func byteConcat(n int, str string) string {
buf := make([]byte, 0)
for i := 0; i < n; i++ {
buf = append(buf, str...)
}
return string(buf)
}
func preByteConcat(n int, str string) string {
buf := make([]byte, 0, n*len(str))
for i := 0; i < n; i++ {
buf = append(buf, str...)
}
return string(buf)
}
func benchmark(b *testing.B, f func(int, string) string) {
var str = randomString(10)
for i := 0; i < b.N; i++ {
f(10000, str)
}
}
func BenchmarkPlusConcat(b *testing.B) { benchmark(b, plusConcat) }
func BenchmarkSprintfConcat(b *testing.B) { benchmark(b, sprintfConcat) }
func BenchmarkBuilderConcat(b *testing.B) { benchmark(b, builderConcat) }
func BenchmarkBufferConcat(b *testing.B) { benchmark(b, bufferConcat) }
func BenchmarkByteConcat(b *testing.B) { benchmark(b, byteConcat) }
func BenchmarkPreByteConcat(b *testing.B) { benchmark(b, preByteConcat) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
结果如下:
go test -bench=. -benchmem .
goos: darwin
goarch: arm64
pkg: main/demo
cpu: Apple M4 Pro
BenchmarkPlusConcat-12 36 32238928 ns/op 530999857 B/op 10045 allocs/op
BenchmarkSprintfConcat-12 20 56848854 ns/op 833393446 B/op 34184 allocs/op
BenchmarkBuilderConcat-12 23143 51218 ns/op 514804 B/op 23 allocs/op
BenchmarkBufferConcat-12 29152 40997 ns/op 368578 B/op 13 allocs/op
BenchmarkByteConcat-12 19549 60984 ns/op 621301 B/op 24 allocs/op
BenchmarkPreByteConcat-12 50913 24879 ns/op 212994 B/op 2 allocs/op
PASS
ok main/demo 10.263s
1
2
3
4
5
6
7
8
9
10
11
12
13
+号和fmt.Sprintf性能差的原因:
strings.Builder/bytes.Buffer表现好的原因
预分配[]byte最佳的原因
通过测试我们发现:
在实际开发中,应根据场景选择合适的方法,在代码可读性和性能之间取得平衡。
在Go语言中,常见的字符串拼接方式有以下6种:
本文主要是针对上面各种方式进行基准测试,帮助我们在以后的编码过程中选择最优的方式。
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randomString(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func plusConcat(n int, str string) string {
s := ""
for i := 0; i < n; i++ {
s += str
}
return s
}
func sprintfConcat(n int, str string) string {
s := ""
for i := 0; i < n; i++ {
s = fmt.Sprintf("%s%s", s, str)
}
return s
}
func builderConcat(n int, str string) string {
var builder strings.Builder
for i := 0; i < n; i++ {
builder.WriteString(str)
}
return builder.String()
}
func bufferConcat(n int, s string) string {
buf := new(bytes.Buffer)
for i := 0; i < n; i++ {
buf.WriteString(s)
}
return buf.String()
}
func byteConcat(n int, str string) string {
buf := make([]byte, 0)
for i := 0; i < n; i++ {
buf = append(buf, str...)
}
return string(buf)
}
func preByteConcat(n int, str string) string {
buf := make([]byte, 0, n*len(str))
for i := 0; i < n; i++ {
buf = append(buf, str...)
}
return string(buf)
}
func benchmark(b *testing.B, f func(int, string) string) {
var str = randomString(10)
for i := 0; i < b.N; i++ {
f(10000, str)
}
}
func BenchmarkPlusConcat(b *testing.B) { benchmark(b, plusConcat) }
func BenchmarkSprintfConcat(b *testing.B) { benchmark(b, sprintfConcat) }
func BenchmarkBuilderConcat(b *testing.B) { benchmark(b, builderConcat) }
func BenchmarkBufferConcat(b *testing.B) { benchmark(b, bufferConcat) }
func BenchmarkByteConcat(b *testing.B) { benchmark(b, byteConcat) }
func BenchmarkPreByteConcat(b *testing.B) { benchmark(b, preByteConcat) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
结果如下:
go test -bench=. -benchmem .
goos: darwin
goarch: arm64
pkg: main/demo
cpu: Apple M4 Pro
BenchmarkPlusConcat-12 36 32238928 ns/op 530999857 B/op 10045 allocs/op
BenchmarkSprintfConcat-12 20 56848854 ns/op 833393446 B/op 34184 allocs/op
BenchmarkBuilderConcat-12 23143 51218 ns/op 514804 B/op 23 allocs/op
BenchmarkBufferConcat-12 29152 40997 ns/op 368578 B/op 13 allocs/op
BenchmarkByteConcat-12 19549 60984 ns/op 621301 B/op 24 allocs/op
BenchmarkPreByteConcat-12 50913 24879 ns/op 212994 B/op 2 allocs/op
PASS
ok main/demo 10.263s
1
2
3
4
5
6
7
8
9
10
11
12
13
+号和fmt.Sprintf性能差的原因:
strings.Builder/bytes.Buffer表现好的原因
预分配[]byte最佳的原因
通过测试我们发现:
在实际开发中,应根据场景选择合适的方法,在代码可读性和性能之间取得平衡。