Sixth phase of /root. How code actually runs underneath.

Most engineers ship code that works. Senior engineers ship code that works and is fast enough. This phase installs the muscle that separates the two: profiling (finding the hot path), memory awareness (knowing what allocates and where), and the discipline of measuring before optimizing. By phase end you can profile a slow function in either Python or Go, identify the actual bottleneck, and articulate the fix in terms a reviewer can verify.

This phase doesn’t ship new OSS. It profiles sift and pulse from earlier phases, identifies the actual bottlenecks, fixes them, and produces profiling reports as artifacts. The reports are themselves portfolio material: they’re proof you can reason about performance from data, not from instinct.


Prerequisites

  • Phase 5 complete; sift and pulse refactored
  • Comfort reading flame graphs and basic profiler output
  • You accept: performance intuition is mostly wrong. Measure first, optimize second, verify the optimization with measurement.

Why this phase exists

“Premature optimization is the root of all evil” is the most-misquoted line in engineering (Knuth’s actual point was different). What it should mean: don’t optimize without data. What it often gets used to mean: don’t think about performance ever. Both are wrong.

Senior engineers think about performance all the time, but they back their decisions with measurement. They know which operations are cheap (hash lookup), which are expensive (linear scan over a large collection), which are surprising (allocation in a tight loop, regex compilation on every call). They reach for profilers as routinely as they reach for debuggers. This phase builds those reflexes.


The pattern-first frame

Same eight steps as every phase.


1. PROBLEM

Your code is slow, or your code is fast and you want to know why, or your service is using more memory than expected. You need to find the actual bottleneck, fix it, and verify the fix. Without tooling, you guess; with tooling, you measure.

That’s the performance problem. Profilers solve “where is the time spent.” Heap dumps solve “what’s holding memory.” Tracing tools solve “what does a single request actually do across processes.” Each tool answers a specific question; reaching for the wrong tool wastes hours.


2. PRINCIPLES

2.1 Measure first, optimize second

The most reliable performance engineering rule: don’t optimize what you haven’t measured. Intuition is wrong 80% of the time about what the bottleneck is. Profile, find the actual hot path, optimize that, verify.

→ Pattern: profiling-as-practice

Investigate:

2.2 Memory hierarchy and cache effects

CPUs are fast; memory is slow. The cache hierarchy (L1, L2, L3, RAM) determines whether an operation takes 1 nanosecond or 100 nanoseconds. Sequential access (cache-friendly) is dramatically faster than random access for large data. This is invisible to most code but shapes the performance of everything that processes a lot of data.

→ Pattern: memory-allocation

Investigate:

2.3 Allocation and garbage collection

Every memory allocation has a cost, small in absolute terms but cumulative. GC’d languages (Python, Go, JVM) periodically pause to reclaim memory, and high allocation rates make those pauses larger or more frequent.

Investigate:

2.4 Hot paths and cold paths

Most code runs occasionally; some code runs constantly. Performance work targets the hot paths: the inner loops, the request handlers, the per-message processing. Optimizing cold paths is wasted effort.

→ Pattern: hot-paths-cold-paths

Investigate:

2.5 I/O bound vs CPU bound vs memory bound

Workloads come in three flavors. CPU-bound work scales with cores. I/O-bound work scales with concurrency (async, threading). Memory-bound work scales with bandwidth, not cores. Knowing which flavor your workload is determines the right scaling strategy.

Investigate:

2.6 Big-O as a guide, but only a guide

Phase 3 taught Big-O. This phase teaches when Big-O matters and when constant factors dominate. O(n) with a 1000× constant beats O(n log n) for n < 1000. Big-O guides architectural decisions; profiling decides tactical fixes.

Investigate:


3. TRADE-OFFS

DecisionOptionsCost
Optimization timingOptimize now; optimize when slow; never optimizeNow: premature, often wrong. When slow: pragmatic. Never: technical debt.
Memory vs CPUCache results (memory ↑, CPU ↓); recompute (memory ↓, CPU ↑)Trade-off depends on access pattern and memory pressure
Concurrency modelAsync / event loop; threads; processesAsync: I/O-bound. Threads: shared state, GIL-limited in Python. Processes: true parallelism, IPC cost
Allocation strategyPool reuse; allocate-per-callPool: reduces GC, complex. Per-call: simple, GC pressure

4. TOOLS (as of 2026-06)

Python profiling

Go profiling

Reading


5. MASTERY: Profile and fix sift + pulse

5.1 The deliverable

Two profile reports, one for sift, one for pulse, plus the fixes that came from the profiling work. Each report should include:

Volume: ~20-40 hours of profiling + fixing + writing. The result is portfolio material: these reports demonstrate you can do real performance engineering, not just talk about it.

5.2 Operational depth checklist

[ ] Profile sift with cProfile + py-spy on a realistic input
[ ] Generate a flame graph for sift; identify the top 3 functions by time
[ ] Profile pulse with go pprof on a realistic workload
[ ] Generate a flame graph for pulse; identify the top 3 functions by time
[ ] Fix at least one identified hot path in each project
[ ] Write before/after benchmarks proving the fix worked
[ ] Use memray (Python) to find an allocation hotspot in sift
[ ] Use go pprof -alloc_space to find an allocation hotspot in pulse
[ ] Read flame graph traces of a famous OSS project (e.g., the Go runtime) once; just to see scale
[ ] Memorize approximate latency numbers: L1 (~1ns), L2 (~3ns), L3 (~10ns), RAM (~100ns), SSD random (~50µs), datacenter network (~500µs), disk seek (~10ms)

6. COMPARE: Python vs Go on the same problem

Pick one of sift’s functions and reimplement in Go (or pick a pulse function and reimplement in Python). Benchmark both on the same input. What’s the speed difference? More importantly: where does the difference come from? Allocation? Interpreter overhead? Concurrency model?

400-word reflection.


7. OPERATE


8. CONTRIBUTE


What ships from this phase


Learning loop cadence

Week 1     PROBLEM + PRINCIPLES 2.1-2.2 (measure first, memory hierarchy)
           Profile sift with cProfile; first flame graph

Week 2     PRINCIPLES 2.3-2.4 (allocation, hot paths)
           Profile pulse with pprof; first flame graph

Week 3     Identify hot paths in both projects; design fixes

Week 4     Implement fixes; benchmark before/after

Week 5     PRINCIPLES 2.5-2.6 (workload types, complexity in practice)
           Write profile reports for sift + pulse

Week 6     COMPARE: cross-language reimplementation
           OPERATE + CONTRIBUTE

Week 7     Exit Test + buffer

Validation criteria

[ ] sift profile report written + at least one fix applied + before/after benchmarks
[ ] pulse profile report written + at least one fix applied + before/after benchmarks
[ ] All 10 operational depth checks
[ ] Cross-language compare reflection (400 words)
[ ] 2-3 runbooks
[ ] 1+ ADR
[ ] Pattern entries deepened STUB → OUTLINE:
    - profiling-as-practice
    - memory-allocation
    - hot-paths-cold-paths
[ ] Exit Test passed

Exit Test

Time: 3 hours.

Part 1: Profile + Fix (120 min)

Given a small slow program (provided), profile it, identify the bottleneck, apply a fix, prove the fix works with before/after measurements. The program will have an obvious-but-not-easy bottleneck.

Part 2: Articulate (60 min)

~1000 words: “You’re told the production service is slow. Walk through your investigation plan from receiving the report to identifying the bottleneck to recommending a fix. Cite the tools you’d use at each step and the data they’d produce.”


Anti-patterns

Anti-patternWhy
Optimizing without measuringThe bottleneck is almost never where you think it is
Reading flame graphs without learning the conventionsYou’ll miss what’s there. Spend 30 min learning the visualization.
Caching everything because “memory is cheap”Cache invalidation is one of the two hard problems
Ignoring allocation in tight loopsA million tiny allocations IS the bottleneck sometimes
Treating “fast enough” as “fastest possible”Senior engineers know when to stop optimizing

Patterns touched this phase


→ Next: Phase 7: Testing Patterns