Big machines — the 64-core-and-up servers, the historical NUMA supercomputers — replace the shouting with directories. Instead of broadcasting "who has this block?" to everyone, a directory already knows exactly which caches hold each block, and sends coherence messages point-to-point only to those caches. No broadcast, no shared bus, no everyone-listens-to-everything. That is how coherence reaches hundreds of cores.
For every block of memory, the directory keeps a small record: what state is it in, and
which caches currently hold it. The classic encoding of "which caches" is a
sharer bit-vector — one bit per core. If bit
The directory lives with memory — either one central table or, on a NUMA machine, distributed so that each memory node owns the directory entries for its own addresses (the block's home node). When a core misses on a block, it sends its request to that block's home directory, which looks up the entry and does exactly the right thing to exactly the right caches.
Here core 1 wants to write block B. It sends one request to B's home directory. The
directory reads the sharer vector —
This is the whole win. The number of messages scales with the number of actual sharers of a block — usually one or two — not with the total core count. Traffic that snooping made everyone's problem becomes a private conversation between the directory and the handful of caches that care.
| Snooping | Directory | |
|---|---|---|
| How others learn of a write | broadcast on a shared bus | point-to-point messages from the home directory |
| Who is contacted | everyone (all caches snoop) | only the actual sharers |
| Extra storage | none (just the bus) | the directory: ~1 bit per core per block |
| Scales to | a handful of cores | hundreds of cores |
| Latency for a simple miss | low (one bus round-trip) | higher (indirection through the directory) |
| Typical use | desktop / small multicore | big NUMA servers, many-core |
Directories are not free — they add memory overhead and an extra network hop of latency (a miss may go core → directory → owner → back). The bet is that saving bus bandwidth is worth the indirection once the core count is high. For small chips, snooping still wins on simplicity and latency; the crossover is why real systems often use both — snooping within a socket, a directory between sockets.
Let's model a directory for one block on a 4-core machine and count the messages it sends, comparing them to the broadcast a snooping bus would have used. Watch invalidations go only to the set bits:
A sharer bit-vector needs one bit per core per block. At 4 cores that is trivial; at 1024 cores it is 1024 bits (128 bytes) of directory for every 64-byte block — the directory would be twice the size of memory it describes. Real large systems use limited-pointer schemes (store only the first few sharer IDs, since most blocks are shared by very few caches) or coarse-vector schemes (one bit per group of cores, invalidating a whole group at once) or hierarchical directories. All are attacks on the same enemy: the directory's storage growing with core count. It is a classic space-vs-precision trade — spend fewer bits, occasionally invalidate a few extra caches.
It is tempting to think directories are strictly better than snooping. They are not — they trade one cost for another. Snooping spends bus bandwidth; a directory spends storage (the bit-vectors) and latency (an extra hop through the home node, and sometimes a three-way core→directory→owner dance for a single miss). On a 4-core laptop a directory would be pure overhead — the bus is nowhere near saturated, so snooping's lower latency wins. Directories only pay off once broadcast traffic becomes the bottleneck. The right question is never "which is better?" but "how many cores?"