Notes — Module 16: Performance and Profiling¶
These are your personal study notes. Write freely and honestly. Incomplete notes are fine — they show where your understanding still needs work. Return to this file to add insights as they develop over time.
Module: [[go/16. Performance and Profiling]] Topic: [[go]] Date started: YYYY-MM-DD Status: In progress
Concept Map¶
Sketch how the concepts in this module relate to each other. Fill in the Mermaid diagram.
mindmap
root((Performance and Profiling))
Mindset
measure before optimizing
optimize the right thing
correctness first
Benchmarking
testing.B loop
-benchmem (allocs/op, B/op)
-count=N for repeatability
benchstat for comparison
pitfalls: DCE, missing ResetTimer
pprof profiles
CPU (where time is spent)
heap (where memory is allocated)
goroutine (all goroutine stacks)
block (channel/mutex waits)
mutex (lock contention)
Capturing profiles
go test -cpuprofile / -memprofile
runtime/pprof (batch programs)
net/http/pprof (live services)
go tool pprof
top / top -cum
list FuncName
web (flamegraph)
Escape analysis
go build -gcflags=-m
stack vs heap allocation
causes of escape
GC pressure link
Concrete optimizations
preallocate slices/maps
strings.Builder
avoid string/byte copies
sync.Pool
avoid interface boxing
GC tuning
GOGC (frequency)
GOMEMLIMIT (hard cap)
debug.SetGCPercent
PGO
default.pgo file
automatic since 1.21
devirtualization
Alternative: draw this on paper, photo it, and link the image here.
Key Insights¶
The "aha moments" — the things that, once understood, made the rest clear. Be specific: "I finally understood X because Y" is more useful than "X makes sense".
- Profile first, always: I finally understood that the profiler almost always contradicts my intuition about what's slow. The heat map analogy clicked — you can only cool a room if you know which room is hot.
- Allocations are not free: The connection between heap allocations and GC work became concrete when I saw
runtime.mallocgcnear the top of a CPU profile. Every allocation is a future GC job. - Add insights as you discover them
My Understanding¶
Explain the core concepts in your own words, as if teaching them to someone else. If you can't explain it simply, you don't understand it well enough yet.
The Performance Mindset¶
Your explanation here
What I'm still unsure about: (e.g., how to know when I've optimized "enough")
Benchmarking and benchstat¶
Your explanation here
What I'm still unsure about: (e.g., how many -count repetitions are needed for statistical significance)
pprof profiles¶
Your explanation here
What I'm still unsure about: (e.g., the difference between flat and cum time in top output)
Escape Analysis¶
Your explanation here
What I'm still unsure about: (e.g., when interface boxing specifically triggers an escape)
GC Tuning¶
Your explanation here
What I'm still unsure about: (e.g., how GOGC and GOMEMLIMIT interact when both are set)
Connections to Other Topics¶
How does this module connect to things you already know?
| This module's concept | Connects to | How |
|---|---|---|
| Heap allocations and GC pressure | [[memory-management]] | Go's tricolor GC is the reason allocation count matters; every heap object is scanned by the GC |
| Benchmarking with testing.B | [[go/11. Testing and Benchmarking]] | The profiling tools in this module are extensions of the benchmark infrastructure from Module 11 |
| sync.Pool | [[go/9. Concurrency]] | Pool is a sync primitive; understanding that it is goroutine-safe but not a persistent cache requires concurrency knowledge |
| GOGC / GOMEMLIMIT | [[go/17. Runtime Internals and the Memory Model]] | GC tuning only makes sense once you understand how the GC works internally |
| Preallocating slices | [[go/4. Composite Types]] | make([]T, 0, n) pre-allocates the backing array; understanding append's growth strategy explains why this matters |
Questions That Arose¶
Log questions as they appear. Don't stop to answer them now — just capture them. Then move the serious ones to QUESTIONS.md.
- Does
sync.Poolguarantee that the object I Put in is the same one I Get back? → added to QUESTIONS.md as Q001 - How does the compiler decide the inlining budget? Is there a way to check it per function? → might be in
go build -gcflags=-m -moutput - Can I use
net/http/pprofwith a custom HTTP mux (likechiorgorilla/mux)? → likely yes, need to confirm
Code Snippets Worth Remembering¶
Patterns, idioms, or examples that captured something important.
Benchmark with DCE protection and ResetTimer¶
var sink any // package-level sink prevents dead-code elimination
func BenchmarkX(b *testing.B) {
input := setupExpensiveInput()
b.ResetTimer() // exclude setup from measurement
var result []byte
for range b.N {
result = processInput(input)
}
sink = result // prevent DCE
}
Why I'm saving this: The two-line pattern (sink + ResetTimer) appears in every correct production benchmark. Missing either one makes the benchmark untrustworthy.
Capture and read a heap profile in one shot¶
Why I'm saving this: The -http flag opens the browser UI directly — no interactive shell needed. Faster for a quick look.
strings.Builder with Grow¶
var sb strings.Builder
sb.Grow(estimatedBytes) // single allocation for the final string
for _, s := range parts {
sb.WriteString(s)
}
return sb.String()
Why I'm saving this: Grow reduces strings.Builder to exactly one allocation (the final buffer). Without it, Builder still amortizes but may do 2–3 internal reallocations.
GOMEMLIMIT + GOGC for containerized services¶
Why I'm saving this: Higher GOGC reduces GC CPU cost; GOMEMLIMIT guards against OOM in a container with a 512Mi limit. This is the production-recommended pairing.
What Tripped Me Up¶
Mistakes I made, misconceptions I had, things that confused me more than they should have. Being honest here helps you later.
- Dead-code elimination in benchmarks — I initially thought assigning to
_was enough to prevent DCE. It is not —_is a complete discard. You need a package-level sink variable. - flat vs cum in pprof top — I initially read
cumas "the function is slow." It actually means "this function and everything it calls." A function with high cum but low flat is a caller of slow code, not slow itself.
Summary in My Own Words¶
Write a 3–5 sentence summary of this entire module without looking at any notes. If you can't do this, you need more study time.
Write your summary here after completing the module.
Last updated: YYYY-MM-DD