4 ms·
The majority of the performance difference between strings concat and builder in your example is explained by memory allocation. Every loop of concat will resul
by aodin 9y ago
The majority of the performance difference between strings concat and builder in your example is explained by memory allocation. Every loop of concat will result in a new allocation, while the builder - which uses []bytes internally - will only allocate when length equals capacity, and the newly allocated slice will be approx. twice the capacity of the old slice (see: https://golang.org/src/strings/builder.go?#L62 https://golang.org/src/strings/builder.go?#L62).
Therefore, 500,000 rounds of concat is about 500,000 allocations, while 200,000,000 rounds of builder is ~ 27.5 allocations (=log2(200000000)).
I would suggest a different benchmark to approximate real world usage:
func BenchmarkConcatString(b *testing.B) {
for n := 0; n < b.N; n++ {
var str string
str += "x"
str += "y"
str += "z"
}
}
func BenchmarkConcatBuilder(b *testing.B) {
for n := 0; n < b.N; n++ {
var builder strings.Builder
builder.WriteString("x")
builder.WriteString("y")
builder.WriteString("z")
builder.String()
}
}
Which still shows a significant performance advantage for using builder (-40% ns/op):
BenchmarkConcatString-4 20000000 93.5 ns/op
BenchmarkConcatBuilder-4 30000000 54.6 ns/op
- marcus_holmes 9y agoWon't the compiler just ignore the "builder.String()" line unless the return value is actually used?
- zaarn 9y agoEasy fix, use the anonymous variable; _ = builder.String() The compiler should not optimize that out.
- majewsky 9y agoThat's absolutely the same as the statement without assignment from a data flow POV. However, the compiler will likely not optimize it away since it would be too burdensome to proof that strings.Builder.String() does not have any side effects. The Go compiler prides itself with fast compilation speed, so I would not expect it to perform cross-package control/data flow analyses.