Mechanical sympathy
The hardware under your abstractions: CPU caches, branch prediction, virtual memory, thread scaling, and why async is not parallelism.
Read in order: later instalments assume the earlier ones. 1 more instalment in the publish queue.
- 01 CPU Caches and Spatial Locality: Why an Array is 3x Faster Than a Linked List for the Exact Same Big-O Complexity
Why arrays are faster than linked lists on real CPUs: cache lines, spatial locality, hardware prefetchers, and pointer chasing.
- 02 Branch Prediction: Why an if Inside a Hot Loop Costs Milliseconds
How CPU pipelining and branch predictors work, why mispredictions flush the pipeline, and how sorting, branchless code, and loop unrolling help.
- 03 RSS vs VSZ in Virtual Memory: What the OOM Killer Actually Counts
malloc() grows VSZ, page faults commit RSS, and the Linux OOM killer only counts RSS. How virtual memory and lazy allocation decide which process dies.
- 04 Thread-per-Core Architecture: Why Extra Threads Eventually Destroy Throughput
Oversized thread pools stall: timeslicing, context switches, cache thrashing. Thread-per-core, CPU pinning, and async I/O match physical cores.
- 05 Bloom Filters vs Counting Bloom Filters: When Deletions Kill Performance
Why counting (deletable) Bloom filters often lose in production: cache misses, random memory access, and better alternatives like hash tables or Cuckoo filters.
- 06 Cuckoo Filters: Cache-Friendly Membership Checks With Deletions
How Cuckoo filters work: fingerprints, two-bucket lookups, kick-out insertions, why they stay cache-friendly, and the real tradeoff of insertion failure.
- 07 Async does not mean parallel, and confusing them costs you a thread pool
Why an event loop with one thread can serve ten thousand connections and still be destroyed by one CPU bound handler, and why marking a method async does nothing for work that never waits.
- 08 The GIL is not why your Python is slow
Why the interpreter lock is released during almost everything a web service does, what the profile actually says when a service is slow, and the narrow case where the GIL is genuinely the bottleneck.
- 09 Thread pool starvation from nested async calls
Why a task that waits on another task in the same pool deadlocks with no error, why the common ForkJoinPool makes this a shared fate across libraries, and why the fix is separate pools rather than bigger ones.
- 10 Immutability is a concurrency strategy, not a style preference
Why a shared mutable object is a lock you forgot to take, why defensive copying is the tax you pay for not deciding, and why the allocation cost is smaller than the synchronisation it removes.
- 11 Your p99 is a garbage collector setting
Why a tail latency that no profiler explains is usually a stop the world pause, why allocation rate matters more than heap size, and why a bigger heap can make the tail worse.
- 12 Heap looks fine, pod still OOM killed
Why a JVM with a 2GB heap needs considerably more than 2GB of container memory, which native regions grow without appearing on any heap graph, and why the kernel kills you without a Java exception.