15 — Performance¶
Go's answer to "is it fast?" is a tool, not an opinion: go test -bench measures, testing.AllocsPerRun counts, -gcflags=-m says what the compiler inlined, -gcflags=-d=ssa/check_bce says which index checks it kept, and runtime/pprof names the function the time went to. This chapter is about reading what those tools print, and about which of their numbers are worth keeping. The library's rule bites hardest here: a key records only what cannot vary, and a nanosecond varies — so the answer keys hold allocs/op and B/op, Found IsInBounds counts, can inline lines, the name in a profile's top row and exit statuses, while every ns/op, MB/s and percentage lives in a Real runs — fence produced by a demo/ script beside the page.
It builds on Memory and the runtime — escape analysis is why an allocation count is what it is, and the collector is who pays for it — and on the go test habits of Testing concurrent code; Testing covers go test itself. The reader should be at ease with a module, a _test.go file and a go build flag.
| Lesson | The one thing |
|---|---|
allocs/op is a key, ns/op is not |
eight concatenations are 8 allocs/op and 880 B/op on every machine, a Builder with Grow is 1 and 256; ns/op is a sample; b.Loop runs the benchmark function once |
AllocsPerRun counts allocations |
a whole number per call from an ordinary program — 4 vs 1, 2 vs 1 vs 0, 5 vs 1, and 0 or 1 for boxing an int depending on its value |
| The compiler says what it inlines | -m lists what can be inlined and what was; only -m=2 says why not — DEFER, recover, cost over the budget of 80, go:noinline |
| Bounds checks, and how to drop them | check_bce prints one line per check kept; a range loop, a masked index or a _ = s[3] hint takes four checks to one, or to none |
| A CPU profile names the hot function | pprof -top puts main.hot in the first row on every run; everything else in the table — samples, duration, percentages — is a real run |
| Companion | What it holds |
|---|---|
| Performance: the compiler errors | the benchmark signature the go command refuses, b.Loop left early or nested, mistyped -gcflags, and the runs that measured nothing and said ok |
| Performance: what go vet and gofmt catch | tests, unusedresult, printf; copylocks staying silent on a copied strings.Builder while the Builder panics; and what no tool catches |
| Performance: resources | the testing documentation, the compiler's source for its budgets, the profiling posts, the books' benchmarking sections, and the pre-1.24 advice to read with care |
Planned¶
benchstat: comparing two sets of runs with a confidence interval — it lives ingolang.org/x/perf, outside the standard library this library runs.- PGO end to end: a profile saved as
default.pgo, the build that uses it, and a-m=2report showing a hot call inlined past the budget of 80. sync.Pooland reuse: when a pool cutsallocs/opand when it only moves the cost; the sync chapter's Planned list names it too.- Execution traces:
runtime/traceandgo tool traceon a program whose problem is waiting rather than computing.