Thoughts Heap

A Blog by Roman Gonzalez.-

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.