โ† back to journal

How many loads and stores can the M6 keep in flight?

2026-10-12 ยท Kit Wickham

This follows the out-of-order window post and uses the same test with different fillers. Every load and store a core has started but not retired needs an entry in a load or store queue, and those queues are much smaller than the window. The short version: a Super core can hold about 137 stores and about 487 loads in flight, a Performance core about 102 stores and 220 loads, and an Efficiency core about 58 stores and 64 loads. The same test also explains the slow creep before each step that I couldn't explain last time.

The test

As before: two independent pointer chases that miss to DRAM, with N fillers after each miss. While the second miss fits behind the first one's fillers, the two overlap and an iteration costs one DRAM latency (about 90 ns). Once it doesn't fit, they serialize and the cost doubles. New fillers:

Loads into integer registers turned out to be the wrong filler: on a Super core they step at about 400, the same place as the integer adds did, because the core runs out of integer registers before it runs out of load-queue entries.

Super cores

storestore + noploadload + nop
Nanoseconds per loop iteration on the Super cores, median per N, against the number of fillers after each load. About 90 is two overlapped DRAM misses, about 190 is two serialized ones. Dashed lines have a nop between each load or store. Points with fewer than two usable runs are left out.

The creep before the step

Each limit shows up twice: a gentle ramp that starts at about half the size, then the step at the full size. Stores ramp from N of 72 and step at 137. Loads ramp from about 250 and step at about 487. The integer adds in the last post had a small rise at about 210 before their step at 400, and the FP adds crept up from about 450 before stepping at 880. I called both unexplained. They're the same thing.

The step is where load B no longer fits behind load A's N fillers. But the next iteration's load A also has to fit, behind B's fillers, while A's own fillers are still waiting to retire. Once both groups of fillers together, 2N, are more than the structure holds, the next A can only get in after the oldest group starts retiring, which happens when the first miss comes back. So it starts a little late, by an amount that grows with the overflow, and the iteration time rises in a straight line until the step. With a limit of Q, the ramp starts near Q/2 and the step comes at Q, and every filler type here fits that.

Performance and Efficiency cores

Same method, with the same core fingerprint as the earlier posts to sort runs by tier. Steering onto Performance cores is unreliable, so those rows are fewer and noisier. The Efficiency runs ran at about 1 GHz, which stretches the timings but not the counts, since N is in instructions.

SuperPerformanceEfficiency
stores in flight~137~102~58
loads in flight~487~220~64
window (last post)~1,050>445~185
integer results (last post)~400~210>185

What I'm unsure about

The takeaway: on a Super core, store-heavy code runs out of room after about 137 stores, long before the 1,050-instruction window fills. Loads get about 487 entries, but a load into an integer register hits the integer limit at about 400 first. The smaller cores scale down hard: an Efficiency core holds only about 58 stores and 64 loads.