Skip to content
About
Main content

Clock: chosen for its hit path, not its hit rate

Why do production systems run Clock rather than the policy with the best hit rate?

This is the item most often explained backwards, so state the causality plainly.

requires that every hit move an entry to the head of a shared list. A hit is the fast path, the thing you are trying to optimize. Under LRU a hit becomes a mutation of global state, which means a lock or at least a set of contended cache-line writes. At a few million buffer accesses per second across dozens of cores, the list head is a single bouncing between sockets. The scalability-law term for the cost of keeping many participants consistent (the "coherency" term, β) is not a metaphor here. It is the probability that two threads hit the pool at the same time, and it is about 1.

Clock removes the shared structure.

  • Each frame carries a reference bit (or a small counter). A hit sets it. That is a local, non-atomic write to the frame's own metadata, touching nothing another core wants.
  • Eviction runs a hand around the frames in a circle. If the bit is set, the hand clears it and moves on. The first frame whose bit is clear is the victim.
// Clock eviction (sketch). A hit does only:   frame[i].ref = 1;
int clock_pick_victim(Frame *frame, int n, int *hand) {
    for (;;) {
        Frame *f = &frame[*hand];
        int cur = *hand;
        *hand = (*hand + 1) % n;
        if (f->pin_count > 0) continue;       // pinned frames are never victims
        if (f->ref) { f->ref = 0; continue; } // second chance: clear and move on
        return cur;                           // reference bit clear: evict
    }
}

PostgreSQL's clock-sweep is exactly this idea, with a usage_count that saturates at 5 and is decremented on each pass of the hand.

So: Clock is not adopted because it approximates LRU well. It is adopted because its hit path does not serialize, and . The comparison that matters is not against hit rate. It is (hit rate × miss cost) against (hit-path synchronization × hit rate). The second term grows with the number of cores. That is why the balance has moved steadily toward cheaper policies over three decades of growth in core counts, and will keep moving.

This is the second clear case here of a contended resource where the metadata for an optimization became the contended thing, and the optimization had to be redesigned around its own bookkeeping.

One-line compression: LRU turns every read into a write to shared state. Clock's entire reason for existing is to make a hit touch nothing another core wants.