Overview
- + Number stitching
- fmt stitching
- Join stitching
- buffer stitching
- builder stitching
In the case of less data, these methods are not much different, but when there are many strings to be spliced, it is recommended builder
. The + sign connection is suitable for the concatenation of short, constant strings, because the compiler will optimize
+ Number stitching
s := s1+s2+s3
fmt stitching
s := fmt.Sprintf("%v%v",s1,s2)
Join stitching
Receive an array of strings and convert them into a concatenated string
sList := []string{s1,s2}
s := strings.Join(SList,"")
buffer stitching
Not only can concatenate character strings, but also concatenate bytes, etc.
var b = bytes.Buffer
b.WriteString(s1)
b.WriteString(S2)
s := String()
builder stitching
var b strings.Builder
b.WriteString(s1)
b.WriteString(s2)
s := b.String()
Optimize the builder
Test code
project\stringBuilder\sb.go
package stringBuilder
import "strings"
func StringBuilder(p []string) string {
var b strings.Builder
l:=len(p)
for i:=0;i<l;i++{
b.WriteString(p[i])
}
return b.String()
}
project\stringBuilder\sb_test.go
package stringBuilder
import "testing"
const TESTSTRING = "test,"
/*
初始化函数
生成一个具有N个字符串的数组
*/
func initStrings(N int) []string{
s:=make([]string,N)
for i:=0;i<N;i++{
s[i]=TESTSTRING
}
return s;
}
/*
测试
10个字符串
*/
func BenchmarkStringBuilder10(b *testing.B) {
p:= initStrings(10)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
/*
测试
100个字符串
*/
func BenchmarkStringBuilder100(b *testing.B) {
p:= initStrings(100)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
/*
测试
1000个字符串
*/
func BenchmarkStringBuilder1000(b *testing.B) {
p:= initStrings(1000)
b.ResetTimer()
for i:=0;i<b.N;i++{
StringBuilder(p)
}
}
Test Results
goos: windows
goarch: amd64
pkg: TestProject/stringBuilder
BenchmarkStringBuilder10-4 5163981 228 ns/op 120 B/op 4 allocs/op
BenchmarkStringBuilder100-4 1000000 1150 ns/op 1016 B/op 7 allocs/op
BenchmarkStringBuilder1000-4 107428 11735 ns/op 21240 B/op 13 allocs/op
PASS
builder
Where is slow?
As can be seen from the benchmark results, it is mainly slowed down in multiple memory allocations. If multiple memory allocations cause GC, it will be slower!
builder
Source code
// WriteString appends the contents of s to b's buffer.
// It returns the length of s and a nil error.
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}
To put it simply, it is filled in by append
functions []byte b
. When the b
byte array is expanded, it will cause memory allocation, which makes the operation slower.
Solution
Reduce the number of memory allocations, which is to b字节数组
allocate the size in advancecap
Modify the code
func StringBuilder(p []string,cap int) string {
var b strings.Builder
l:=len(p)
b.Grow(cap)
for i:=0;i<l;i++{
b.WriteString(p[i])
}
return b.String()
}
//测试代码以10个字符串为例
//修改为如下
func BenchmarkStringBuilder10(b *testing.B) {
p:= initStrings(10)
b.ResetTimer()
cap := 10*len(TESTSTRING)
for i:=0;i<b.N;i++{
StringBuilder(p,cap)
}
}
Test results after optimization
BenchmarkStringBuilder10-4 10027047 114 ns/op 64 B/op 1 allocs/op
BenchmarkStringBuilder100-4 1312066 810 ns/op 512 B/op 1 allocs/op
BenchmarkStringBuilder1000-4 141570 8080 ns/op 5376 B/op 1 allocs/op
PASS
Can be optimized 20%
~50%