A driver writes four 32-bit values to consecutive addresses in a frame buffer, and the system turns them into a single 16-byte write, four times faster. The same driver writes four values to a device's command register, one after another, expecting four commands. The device receives one write, with the last value. The buffer that made the frame buffer fast made the device wrong.
How do buffers hide latency, and what does each one hide that someone might need to see?
Latency can be hidden only by not waiting for it: answer early, combine work, or fetch before being asked. Each of these is a buffer that acts on the target's behalf. That is pure gain as long as nobody depends on what the buffer hides: when the write really happened, how many writes there were, whether a read had side effects. For memory, nobody does. For devices, almost everybody does.
Mechanism | Hides | By | Costs in visibility |
|---|---|---|---|
Posting (lesson 5.7) | Write round trip | Sending writes with no response | No status to the requester; no knowledge of when it landed |
Write buffer / early acknowledgement | Write latency to the target | Accepting a write and answering before it reaches the target | "Done" means "accepted", not "arrived"; ordering with other paths (lesson 7.4) |
Write combining | Per-write overhead | Merging several writes to nearby addresses into one larger write | The number, order and size of writes change; fatal for device registers |
Prefetching | Read latency | Reading data before it is requested | Reads that software never issued; wasted bandwidth; side effects on read-sensitive registers |
Caching (Part 13) | Read and write latency | Keeping copies close to the requester | Other agents may see stale data unless coherence keeps copies in step |
The common thread: each buffer replaces "the target did it" with "something on the way did it, or will". Whether that is safe depends on the target, which is why transactions carry attributes saying which of these behaviours are allowed (lesson 5.12).
Target | Posting / early ack | Combining | Prefetching | Caching |
|---|---|---|---|---|
Normal memory | Yes | Yes | Yes | Yes (with coherence) |
Frame buffer or other write-mostly memory | Yes | Yes (write-combining memory) | Rarely useful | Usually no |
Device registers | Only with care (and a read-back to confirm) | No | No | No |
Posting and write buffers turn a write's round trip into a hand-off: write-heavy code runs at the speed of the buffer.
Write combining turns many small writes into few large ones, raising protocol efficiency (lesson 6.9).
Prefetching turns latency-bound reads into bandwidth-bound ones (lesson 6.1), when access patterns are predictable.
Combined device writes: the opening story. The command register's address range was mapped as write-combining memory; four commands became one. Mapping it as device memory (non-bufferable, non-modifiable) fixed it.
Prefetched FIFO pops (illustrative): a prefetcher read ahead into a peripheral's receive-data register, popping bytes nobody asked for.
Early-acknowledged writes and interrupts: a write acknowledged by a buffer, followed by an interrupt on another path, let software read the target before the write arrived (lessons 5.14 and 7.4).
Reading it pops data; a prefetch would consume bytes no software read, losing them.
Not necessarily: the acknowledgement means the buffer accepted it. To know it arrived, use a non-posted access that is ordered behind it, such as a read from the same target.
Posting, write buffers, combining, prefetching and caching hide latency by acting before or instead of the target.
Each changes what can be seen: when a write landed, how many writes there were, which reads happened.
Allow them for memory; exclude device registers by attribute, end to end.