Decades of research have optimized coherence protocols; rather than survey it all, the book picks two optimizations that work for both snooping and directory protocols — effective, and representative of what’s possible.
9.2.1 The migratory sharing optimization
Migratory sharing migratory sharing The pattern where a block is read-then-written by one core after another (classic critical-section data). Plain protocols pay a GetS AND a GetM per hop; migratory optimizations hand out exclusive data on the first read — via a predictor issuing GetM on load misses, or via the MM state. defined in Chapter 9 — open in glossary is the critical-section rhythm: core after core does read-then-write on the same block (the lock variable itself is the canonical case). A plain protocol charges each hop two full transactions — a GetS it immediately regrets, then a GetM. Step through the fix:
plain MSI
coherence transactions: 0
1 / 9The pattern
Block B holds a lock and its protected data. C0 just finished its critical section: B is M in C0. Now C1 enters — it will READ B (acquire, inspect) and then WRITE it (update, release). Then C2 will do the same. This is migratory sharing.
Two implementation routes:
Predictor only — no protocol change
A hardware table records blocks that were fetched with GetS and promptly written; on a load miss to such a block, issue a GetM instead. The hazard is misprediction: taken to the extreme (all loads issue GetM), no two cores can ever share a block read-only.
The MM state migratory m (mm) state A state equal to M for coherence purposes but marking 'obtained exclusively on a PREDICTED migratory read.' Store in MM → M (prediction confirmed). Other-GetS finding M → send exclusive data (predict migration continues); finding MM → pattern broken, send a shared copy and revert to S (or O), so pure readers still get S copies. defined in Chapter 9 — open in glossary — self-correcting
MM = M for coherence purposes (dirty,
exclusive, owned) plus one bit of meaning: “obtained exclusively
on a predicted-migratory read.”
• Store in MM → silently M (prediction confirmed).
• Other-GetS finds M → predict migration: send an
exclusive copy, invalidate self.
• Other-GetS finds MM → the store never came;
pattern broken: send a shared copy, revert to S (or O).
Many pure readers ⇒ everyone gets S, exactly like the plain protocol.
Migratory sharing is one detectable phenomenon; the literature holds many schemes targeting specific patterns — and general frameworks for predicting coherence events.
9.2.2 False sharing optimizations
False sharing false sharing Cores read/write DIFFERENT data that happens to share a cache block, generating real coherence traffic for no real sharing. Likelihood grows with block size. Mitigations: sub-block coherence, or speculatively using stale data while permissions are fetched. defined in Chapter 9 — open in glossary : two cores read and write different data that happen to share a cache block. The sharing is false; the coherence traffic — permission stalls, interconnect load — is entirely real. The likelihood scales with block size (bigger blocks pack more unrelated neighbors) and depends on the workload. Two mitigations that keep the block size:
Sub-block coherence sub-block coherence Maintaining coherence state at sub-block granularity so different sub-blocks of one cache block can be in different states — reduces false sharing at the cost of extra state bits. defined in Chapter 9 — open in glossary
Keep coherence state at a finer, sub-block granularity: one cache block can be M in the sub-block core 0 writes and S in the sub-block core 1 reads. Genuinely removes the false invalidations — at the cost of extra state bits per block.
Speculation
A predictor flags blocks that are invalid because of false sharing; the core then speculatively uses the stale data while the coherence permission is fetched. A correct prediction hides the latency penalty — but the requests still travel: no interconnect traffic is saved.
Check yourself
1.What is migratory sharing, and why does a plain protocol handle it badly?
2.The predictor-only approach (issue GetM instead of GetS on a predicted-migratory load miss) needs no protocol change. What's its danger?
3.A cache holds a block in MM and receives another core's GetS. A cache holds the same block in M and receives a GetS. What's the difference in response?
4.What is false sharing, and which design parameter most directly affects it?
5.Compare the two false-sharing mitigations: what does each cost or fail to fix?