The MESI Protocol

Snooping told us what caches must do — invalidate stale copies over a shared bus. MESI is the classic recipe for how: it tags every cached block with one of four states and lays down precise rules for changing state on each read, write, and overheard bus transaction. Nearly every mainstream processor of the last thirty years — Intel, AMD, and countless others — runs a descendant of MESI. Learn these four letters and you understand the beating heart of multicore coherence.

The name is just the four states: Modified, Exclusive, Shared, Invalid. The whole protocol is the answer to one question asked of every block: do I have the only copy, a shared copy, a dirty copy, or no copy at all?

The four states

Each state records two facts about a block in one cache: whether this cache's copy is the only one, and whether it has been modified (differs from memory).

The quiet star is E. Without it, a core that loads a block and then writes it would have to broadcast on the bus twice — once to read, once to claim the write. The E state lets the hardware notice "I'm the only one who has this" on the read, so the later write costs nothing on the bus. For the extremely common read-then-modify pattern, that is a huge saving.

The state machine

Here is MESI as a diagram — the single most important picture in this module. Watch it build in stages. Solid arrows are transitions caused by this core's own reads and writes; dashed arrows are forced on you when you snoop a remote core's request on the bus.

Trace the happy path: a block starts I. Your core reads it and, finding no other holders, it lands in E. Your core writes it — a silent jump to M, no bus transaction. Later another core reads the block; you snoop that request, write your dirty data back, and drop to S. If instead the other core writes, you snoop the invalidating request and fall to I. Every legal move a block can make is one of these arrows.

Reading the transitions

The full rulebook, in words, for the block in your cache:

FromEventToBus action
Iyour read, no other copiesEBusRd
Iyour read, others hold itSBusRd
Iyour writeMBusRdX (read-for-ownership)
Eyour writeMnone — silent!
Syour writeMBusUpgr (invalidate sharers)
Eremote read (snooped)S
Mremote read (snooped)Swrite back dirty data
S or Eremote write (snooped)I
Mremote write (snooped)Iwrite back dirty data

Simulate it: a two-core trace

Let's drive MESI with an actual request trace and watch both caches' states evolve. One block, two cores; each line is "core X reads" or "core X writes". Notice the E→M write on core 0 that costs no bus traffic, and how core 1's read forces core 0 from M down to S with a writeback.

type State = "M" | "E" | "S" | "I"; const cache: State[] = ["I", "I"]; // two cores, one block function other(c: number): number { return c === 0 ? 1 : 0; } function access(core: number, op: "read" | "write"): void { const o = other(core); let bus = ""; if (op === "read") { if (cache[core] === "I") { // read miss if (cache[o] === "M") { bus = "BusRd + writeback"; cache[o] = "S"; cache[core] = "S"; } else if (cache[o] === "E" || cache[o] === "S") { bus = "BusRd"; cache[o] = "S"; cache[core] = "S"; } else { bus = "BusRd"; cache[core] = "E"; } // no other copy -> Exclusive } else { bus = "(hit)"; } // read hit: state unchanged } else { // write if (cache[core] === "M") { bus = "(hit)"; } else if (cache[core] === "E") { bus = "silent E->M"; cache[core] = "M"; } // no bus! else if (cache[core] === "S") { bus = "BusUpgr (invalidate)"; cache[o] = "I"; cache[core] = "M"; } else { // write miss from Invalid bus = cache[o] === "M" ? "BusRdX + writeback" : "BusRdX"; cache[o] = "I"; cache[core] = "M"; } } console.log(`core ${core} ${op.padEnd(5)} -> [C0=${cache[0]}, C1=${cache[1]}] bus: ${bus}`); } access(0, "read"); // I -> E (core 0 alone) access(0, "write"); // E -> M silent, no bus access(1, "read"); // core 0 M -> S (writeback), core 1 -> S access(1, "write"); // core 1 S -> M, core 0 -> I access(0, "read"); // core 1 M -> S (writeback), core 0 -> S

MESI has one clumsy moment: when a Modified block is shared with another core, MESI forces a writeback to memory to get everyone clean-and-Shared. AMD's MOESI adds an Owned state so one cache can keep the dirty data and hand it cache-to-cache to others without touching memory — the owner stays responsible for eventually writing back. Intel's MESIF adds a Forward state so that, when many caches share a block, exactly one is designated to answer new requests (instead of all of them replying at once). Both are MESI with one extra state bolted on to make cache-to-cache sharing cheaper on large, many-core interconnects. The four core ideas never change.

Because both E and M mean "I'm the only one with this block," beginners blur them. The difference is dirtiness. E is clean — your copy still equals memory, so if you drop it you owe memory nothing. M is dirty — you have changed it and memory is stale, so before anyone else may read it you must write it back. That single bit is what decides whether a remote read costs a writeback (from M) or is free (from E). Silent E→M is legal precisely because moving from clean-exclusive to dirty-exclusive tells no other cache anything it doesn't already know: nobody else had the block anyway.