This document outlines best practices for writing high-performance Go code.
At the moment, it's a collection of links to videos, slides, and blog posts
("awesome-go-performance"), but I would like this to evolve into a longer book
format where the content is here instead of external. The links should be sorted into categories.
All the content will be licensed under CC-BY-SA.
All optimizations should follow these steps:
- determine your performance goals and confirm you are not meeting them
- profile to identify the areas to improve. This can be CPU, heap allocations, or goroutine blocking.
- benchmark to determine the speed up your solution will provide using
the built-in benchmarking framework (http://golang.org/pkg/testing/)
Make sure you're benchmarking the right thing, and benchmarking it on your target operating system and architecture. Benchmarking on your macbook is not the same as benchmarking in a production-like environment.
- profile again afterwards to verify the issue is gone
- use https://godoc.org/golang.org/x/perf/benchstat or
https://github.com/codahale/tinystat to verify that a set of timings
are 'sufficiently' different for an optimization to be worth the
added code complexity.
- use https://github.com/tsenart/vegeta for load testing http services
- make sure your latency numbers make sense: https://youtu.be/lJ8ydIuPFeU
The first step is important. It tells you when and where to start optimizing.
More importantly, it also tells you when to stop. Pretty much all
optimizations add code complexity in exchange for speed. And you can always
make code faster. It's a balancing act.
The basic rules of the game are:
- minimize CPU usage
- do less work
- this generally means "a faster algorithm"
- but CPU caches and the hidden constants in O() can play tricks on you
- minimize allocations (which leads to less CPU stolen by the GC)
- make your data quick to access
This book is split into different sections:
1) basic tips for writing not-slow software
* CS 101-level stuff
2) tips for writing fast software
* Go-specific sections on how to get the best from Go
3) advanced tips for writing really fast software
* For when your optimized code isn't fast enough
- choose the best algorithm
- traditional computer science analysis
- O(n^2) vs O(n log n) vs O(log n) vs O(1)
- this should handle the majority of your optimization cases
- be aware of http://accidentallyquadratic.tumblr.com/
- pre-compute things you need
- add a cache -> reduces work
Techniques applicable to source code in general
- introduction to pprof
- go tool pprof (and https://github.com/google/pprof)
- Writing and running (micro)benchmarks
- -cpuprofile / -memprofile / -benchmem
- How to read it pprof output
- What are the different pieces of the runtime that show up
- Macro-benchmarks (Profiling in production)
Techniques specific to the architecture running the code
- introduction to CPU caches
- building intuition around cache-lines: sizes, padding, alignment
- OS tools to view cache-misses
- (also branch prediction)
- introduction to CPU caches
Comment about Jeff Dean's 2002 numbers (plus updates)
- cpus have gotten faster, but memory hasn't kept up
- Stack vs. heap allocations
- What causes heap allocations?
- Understanding escape analysis
- Using sync.Pool effectively
- cost of calls via interfaces (indirect calls on the CPU level)
- runtime.convT2E / runtime.convT2I
- type assertions vs. type switches
- special-case map implementations for ints, strings
Common gotchas with the standard library
- time.After() leaks until it fires
- Reusing HTTP connections...
- And all the dangers that go with it
- Common uses for unsafe
- mmap'ing data files
- speedy de-serialization
- Performance characteristics of cgo calls
- Tricks to reduce the costs
- Passing pointers between Go and C
- Stuff about writing assembly code for Go
- brief intro to syntax
- calling convention
- using opcodes unsupported by the asm
- notes about why intrinsics are hard
- Popular replacements for standard library packages:
- encoding/json -> ffjson
- net/http -> fasthttp
- regexp -> ragel (or other regular expression package)
- encoding/gob -> https://github.com/alecthomas/go_serialization_benchmarks
- protobuf -> https://github.com/gogo/protobuf
- all formats have trade-offs; choose one that matches what you need
Look at some more interesting/advanced tooling