The Cache Write Buffer's Coalescing Window: Why Repeated Writes to the Same Line Don't Always Reach Memory

2026-06-14

Between the store buffer and the L1 cache (or between L1 and L2 on a write-through design) sits a small structure with a stopwatch: the write coalescing buffer. Its job is to hold dirty cache lines briefly so that multiple writes to the same line can be merged into a single downstream transaction. The "window" is the time budget — once it expires or the buffer fills, the line drains.

This matters because writes are expensive in ways loads aren't. A store that misses L1 triggers a read-for-ownership (RFO): the core must acquire the line in Modified state via the coherence fabric before the write can complete. Each RFO costs coherence traffic, snoop responses, and potentially evicting someone else's copy. If your code writes to the same line 8 times in quick succession, you want one RFO, not eight.

The coalescing buffer has three knobs that determine whether merging happens:

Concrete example: Initializing a 64-byte struct field-by-field with 8 quadword stores. With coalescing, those 8 stores merge into one cache line write that hits L1 once. Without it (say, fences between each store, or stores spread across a long dependency chain), each store may drain individually, costing 8× the L1 bandwidth and potentially 8× the RFO traffic if the line keeps getting stolen back.

The pathology to watch for: a producer-consumer pattern where one core writes a flag and another spins on it. The writer wants coalescing; the reader wants the line drained now. If the writer's coalescing window is 30 cycles and the reader polls every 10 cycles, you've just added ~30 cycles of latency to every flag update — invisible in single-core benchmarks, devastating in lock-free queues.

Rule of thumb: Back-to-back stores to the same 64-byte line cost roughly 1 store-port cycle each + 1 RFO per line. Stores spread across N different lines cost N RFOs. If you can pack writes into the same line and emit them within ~20 cycles of each other, coalescing usually wins. Add a fence between them and you've just disabled the optimization.

Key Takeaway: Write coalescing buffers turn many same-line stores into one memory transaction — but only inside a tight time window that fences, evictions, and buffer pressure can all collapse.

All newsletters