2026-08-21
When a CPU executes a store, the cache has a policy choice: forward the write to the next level immediately (write-through), or absorb it locally and defer the memory update until eviction (write-back). This one bit of policy shapes bandwidth, power, coherence, and failure recovery across the entire memory hierarchy.
Write-through updates the cache line and simultaneously issues the write to the next level. The cache line is never "dirty" — memory is always the source of truth. Simple, easy to reason about, and every store costs a downstream transaction. Pair it with a write buffer so the CPU doesn't stall waiting for DRAM, but the sustained bandwidth to the next level equals the store rate.
Write-back updates only the cache line and sets a dirty bit. The write to memory happens later, when the line is evicted. A hot line that's stored to a thousand times generates one memory write instead of a thousand. The cost: on eviction, a dirty line must be written back before the new line loads, doubling miss latency, and memory is stale until then, which forces the coherence protocol to intervene when another core reads the same address.
Orthogonal policy — write-miss handling: write-allocate loads the missing line into cache before writing (pairs naturally with write-back); no-write-allocate writes straight to memory without loading (pairs with write-through). Streaming writes that won't be re-read benefit from no-write-allocate — you'd just pollute the cache.
Real example: Modern x86 L1 data caches are write-back, write-allocate — they absorb repeated stores to the stack and hot fields without touching L2. But CPUs also expose write-combining memory types for regions like framebuffers, where the software promises the writes are streaming and won't be re-read. WC buffers merge sequential stores into cache-line-sized bursts and skip the cache entirely — a hybrid that avoids both cache pollution and per-store bus traffic.
Rule of thumb: The bandwidth ratio between write-through and write-back on a workload with reuse factor R (stores per line before eviction) is roughly R:1. If your code stores to the same 64-byte line 8 times before it's evicted, write-back cuts downstream write bandwidth by 8×. This is why every modern L1D uses write-back — even a modest reuse factor collapses memory bandwidth demand by an order of magnitude.
The tradeoff is coherence complexity: write-back caches need MESI-style protocols to answer "who has the current value?" because it's no longer always memory.
