4.7 KiB
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.
Optimization Workflow
-
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.
- profile again afterwards to verify the issue is gone
- use https://godoc.org/rsc.io/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:
- basic tips for writing not-slow software * CS 101-level stuff
- tips for writing fast software * Go-specific sections on how to get the best from Go
- advanced tips for writing really fast software * For when your optimized code isn't fast enough
Basics
- 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/
- https://agtb.wordpress.com/2010/12/23/progress-in-algorithms-beats-moore%E2%80%99s-law/
- pre-compute things you need
- add a cache -> reduces work
Introductory Profiling
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)
- net/http/pprof
Tracer
Advanced Techniques
-
Techniques specific to the architecture running the code
-
introduction to CPU caches
- building intuition around cache-lines: sizes, padding, alignment
- false-sharing
- OS tools to view cache-misses
-
(also branch prediction)
-
Comment about Jeff Dean's 2002 numbers (plus updates)
- cpus have gotten faster, but memory hasn't kept up
Heap Allocations
- Stack vs. heap allocations
- What causes heap allocations?
- Understanding escape analysis
- Using sync.Pool effectively
Runtime
- cost of calls via interfaces (indirect calls on the CPU level)
- runtime.convT2E / runtime.convT2I
- type assertions vs. type switches
- defer
- special-case map implementations for ints, strings
Common gotchas with the standard library
- time.After() leaks until it fires
- Reusing HTTP connections...
- ....
Unsafe
- And all the dangers that go with it
- Common uses for unsafe
- mmap'ing data files
- speedy de-serialization
cgo
- Performance characteristics of cgo calls
- Tricks to reduce the costs
- Passing pointers between Go and C
Assembly
- Stuff about writing assembly code for Go
- brief into to syntax
- calling convention
- using opcodes unsupported by the asm
- notes about why intrinsics are hard
Alternate implementations
- Popular replacements for standard library packages:
- encoding/json -> ffjson
- net/http -> fasthttp
- regexp -> ragel (or other regular expression package)
- serialization
- 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
Tooling
Look at some more interesting/advanced tooling
- perf (perf2pprof)
- go-torch (+flamegraphs)