The last page ended on a threat:
Everything you learned about caches applies. The TLB is a small (often 64–1536 entry), usually highly- or fully-associative cache. Its "block" is a single page's translation. You look up a VPN; a TLB hit returns the PPN in about a cycle, and translation is done. A TLB miss triggers the page-table walk — which then fills the TLB with the translation it found, so the next access to that page hits. Locality does the rest: because programs touch the same pages over and over (temporal) and nearby pages (spatial), a tiny TLB captures the overwhelming majority of translations.
On modern chips the walk is done by a hardware page-table walker — a dedicated state machine — so a TLB miss doesn't even interrupt the OS; software only gets involved on an actual page fault.
A crucial figure of merit is TLB reach — the total memory the TLB can map without a miss:
A 64-entry TLB with 4 KB pages reaches only
Now the elegant part. A first-level cache faces a chicken-and-egg problem: should it be indexed by virtual or physical addresses? Virtual is fast (no translation first) but causes aliasing headaches; physical is clean but seems to force you to wait for the TLB before you can even start the cache lookup — serialising two slow steps. The resolution is VIPT — virtually indexed, physically tagged.
The key insight from the last page: the page offset bits are not translated, so they are known immediately. If the cache's index and block offset fit entirely within those untranslated page-offset bits, the L1 can start indexing on them right now — reading out the candidate tags — while the TLB translates the VPN in parallel. When the TLB delivers the physical page number a moment later, its physical tag is compared against the tags the cache already fetched. Translation and cache access overlap completely; the TLB latency vanishes into the shadow of the cache read.
There is a price: the trick only works if
Naively, yes: process B's virtual addresses mean different physical pages than process A's, so switching processes would require flushing the whole TLB — and then B stalls through a storm of cold TLB misses rebuilding it. That flush cost made context switches painful for years. The fix is address-space identifiers (ASIDs), sometimes called PCIDs on x86: each TLB entry is tagged with the ID of the process it belongs to, so entries from A and B coexist and only the matching process's translations hit. Now a context switch just changes the current ASID — no flush, and when you switch back to A its translations are often still warm. A few tag bits per entry buy back the whole flush.
They are easy to conflate because both are "misses", but they concern different things. A cache miss means the data isn't in the cache. A TLB miss means the translation isn't in the TLB — the data might be sitting right there in L1, but you can't form its physical address to find it. All four combinations occur: TLB-hit + cache-hit (ideal), TLB-hit + cache-miss, TLB-miss + cache-hit (translate first, then the data's already there), and TLB-miss + cache-miss. And a page fault is a third, far rarer beast still — the page isn't in physical memory at all. Keep the three levels — translation, cache, physical presence — separate in your head.
The TLB is the last piece that keeps the fast path fast. Everything above it — caches, translation — has
assumed a "main memory" that eventually answers. What that memory actually is, and why its latency
has barely improved in twenty years, is the floor of the whole hierarchy: