All posts
One real model, one ordinary GPU: turn a single knob until something breaks, report the number, and read the trace that explains why. New parts publish weekly.
| Primer | How an LLM actually serves a request | the machine the series breaks: weights, KV cache, prefill, decode, batching |
| Primer 2 | What actually happens inside an LLM | opens the forward-pass black box: token to vector, what a block does, query/key/value, the KV cache, next-token prediction |
| Part 1 | Hit the wall | an 8.6 GB model on a 23 GB GPU tops out at 7 req/s — and the wall isn’t memory |
| Part 2 | The prefill freeze | one fat prompt stalls everyone’s stream; one scheduler flag cuts the stutter 2.4x |
| Part 3 | The batching cliff | turning batching off drops the server 22x; the win flattens at batch ~64 |
| Part 4 | The attention sink | on a CPU, a deep head pours its whole attention onto the first token, whose state towers 12x over the rest, and why that limits what you can evict from a long context |
| Part 5 | Starving the cache | flood vLLM until the KV cache binds and it preempts on every workload, not just the pathological one; the cost is tail latency you can see coming, not a crash |
| Part 6 | Quantization to make a model fit | a 9B that fp16 can’t serve on this GPU is quantized to 4-bit and serves at ~75% of a 4B’s speed, because decode moves bytes, not parameters |
| Part 7 | Speculative decoding | a flag that runs single-stream generation 1.8x faster cuts a flooded server’s throughput from 1,096 to 473 tok/s and triples time-to-first-token, because speculation multiplies the per-request work batching can’t share and eats concurrency on a hybrid model |
| Part 8 | FlashAttention | the same attention kernel is 3 ms faster at 256 tokens and 6 s faster at 30,000, because it cuts quadratic memory traffic to linear and splits the long decode read across the GPU; the payoff scales with prompt length, not a fixed percentage |
| Part 9 | Off the rig: decode on a CPU | coming soon |
Every post ends with a reproduce section; the scripts, raw logs, and profiler traces
live in the companion repo
under experiments/ and benchmarks/, one folder per part.
Disclaimer: This blog is written and published in my personal capacity. The opinions, findings, and conclusions expressed herein are solely my own and do not necessarily represent the views, policies, or endorsements of my current or past employers.