Golang’s Sprintf luxury tax
I wanted to get a sense of how expensive the different mechanisms to build strings are; I’m used to use printf
like functions in the languages I work with, and I wanted to check how performant (or not) was in Go. I came up with this benchmark code:
Which gave me the following results when running the code
$ go test -bench=. -benchmem 2>&1
goos: linux
goarch: amd64
BenchmarkSprintfGreeter-8 20000000 100 ns/op 32 B/op 2 allocs/op
BenchmarkBuilderGreeter-8 30000000 48.9 ns/op 24 B/op 2 allocs/op
BenchmarkConcatGreeter-8 100000000 16.9 ns/op 0 B/op 0 allocs/op
BenchmarkBuilderReplicate2-8 20000000 80.8 ns/op 56 B/op 3 allocs/op
BenchmarkConcatReplicate2-8 20000000 114 ns/op 64 B/op 3 allocs/op
BenchmarkBuilderReplicate4-8 10000000 128 ns/op 120 B/op 4 allocs/op
BenchmarkConcatReplicate4-8 5000000 268 ns/op 176 B/op 7 allocs/op
BenchmarkBuilderReplicate8-8 10000000 191 ns/op 248 B/op 5 allocs/op
BenchmarkConcatReplicate8-8 3000000 607 ns/op 528 B/op 15 allocs/op
BenchmarkBuilderReplicate16-8 5000000 287 ns/op 504 B/op 6 allocs/op
BenchmarkConcatReplicate16-8 1000000 1595 ns/op 1712 B/op 31 allocs/op
BenchmarkBuilderReplicate32-8 3000000 553 ns/op 1016 B/op 7 allocs/op
BenchmarkConcatReplicate32-8 300000 3770 ns/op 6064 B/op 63 allocs/op
PASS
ok _/home/roman/Projects/playground 23.791s
From what we can tell from the Greeter
benchmarks, in the simplest use case, a Sprintf
is twice slower than using strings.Builder
, with just concatantion using +
being almost 10x faster. This is in the case of a single string build (no iterations).
In the scenario where we are accumulating a string in a loop, we can see the benefits of using strings.Builder
vs. +
at four iterations, where strings.Builder
is around 38% faster.
So, the lessons learned are:
Avoid
Sprintf
if you can make do with string concatenation (or if performance is not that important compared to convenience);Always use
strings.Builder
when dealing with string accumulation in loops.