Memory grew 100× in a decade while TLBs merely tripled, so big-data applications drown in page walks — four dependent memory accesses each, up to 24 under virtualization — and this paper attacks both factors of the cost, merging pairs of 4 KB page-table levels into single 2 MB nodes so a walk needs fewer indirections (one access with walker caches, opportunistically and with graceful fallback when big allocations fail), and biasing cache replacement to keep page-table entries during high-TLB-miss phases (when data caching was failing anyway, and the whole table fits in the LLC) — together making most walks single-access cache hits for +9.2% native, +14.0% virtualized, and 8.7% less dynamic cache energy.
The argument in one glance
-
Problem
Translation has fallen behind memory
Every memory access needs its virtual address translated to a physical one. The TLB caches translations, but its reach — about 1,500 entries, 3 GB even with 2 MB pages — covers a shrinking sliver of modern memories, which grew 100× in the same decade TLBs tripled. Each miss walks the page table: a 4-level radix tree of 4 KB nodes, four serial pointer-chasing memory accesses. Inside a virtual machine every guest level itself needs host translation — a two-dimensional walk of up to 24 accesses. Coming 5-level tables deepen the tree further.
-
What people did
Restructure the table, or cache the walk
Hardware already fights back with page walker caches (PWCs) that remember upper-level entries, cutting the four accesses to about 1.5 on average. Research proposals went further: POM_TLB puts a giant TLB in DRAM; ASAP prefetches lower table levels; Elastic Cuckoo Hashing replaces the tree with hash tables to eliminate pointer chasing; CSALT biases caches toward translation entries for its DRAM-TLB.
-
Why it fell short
The clever layouts assume allocations the kernel cannot promise
ECH and ASAP only function if the kernel can hand out large physically-contiguous regions on demand — and a stress test in this paper shows 2 MB allocations failing 0.5% of the time at mild memory oversubscription and 12% under heavy load. A design that has no fallback when that happens is not deployable. Meanwhile the PWC's very success hides where the remaining cost lives: the leaf levels it cannot cache, whose entries also keep missing in the data caches — the average walk still costs 50.9 cycles, and virtualized walks still average 4.4 memory accesses.
-
This paper's idea
Fewer accesses, and make the survivor a cache hit
From Uppsala University and Arm: two complementary, deliberately boring-to-deploy techniques. FLATTENING merges two levels of 4 KB page-table nodes into one 2 MB node (512 × 512 = 262,144 entries), halving the tree's depth — opportunistically, per node, falling back to conventional 4 KB pairs whenever a 2 MB allocation fails. With the PWC, native walks need just 1.0 memory access; virtualized 2D walks drop from 4.4 to 2.8. PRIORITIZATION biases L2/LLC replacement to keep page-table entries during high-TLB-miss phases — precisely when data caching is failing anyway (95% L2 data miss rates) and the whole 16 MB table of an 8 GB workload fits in the LLC. Results: +2.3% (flattening) + 6.8% (prioritization) = +9.2% native; +14.0% virtualized with 8.7%/4.7% dynamic cache/DRAM energy savings; kernel prototype in +614/−109 lines; hardware cost roughly two bits per entry and a per-line tag.
Why this problem is worth solving
Address translation is the tax every single memory access
pays. When the
Translation Lookaside Buffer — a small hardware
cache of recent virtual-to-physical translations, consulted on
every load and store. A modern L2 TLB holds about 1,500
entries; even mapping 2 MB pages that covers only ~3 GB, so
applications with tens of gigabytes of hot data miss
constantly
the operating system’s map from virtual
to physical addresses, organized as a 512-ary radix tree: four
levels of 4 KB nodes holding 512 eight-byte entries each. A
walk uses successive 9-bit slices of the virtual address to
index each level — four dependent memory accesses, each
waiting for the previous one, before the data access itself
can even begin
Virtualization compounds the tax brutally. The guest’s page
table maps guest-virtual to guest-physical, but every
guest-physical address — including the addresses of the guest
page table’s own nodes — must itself be translated by the
host’s table. The result is the
the nested translation a virtual
machine pays: each of the guest walk’s four accesses first
requires a full four-access host walk, plus one final host
walk for the data address — 5 × 4 + 4 = 24 memory accesses in
the worst case
What people did before, and where it broke
- Page walker caches (the baseline, and genuinely excellent). Intel-style paging-structure caches remember upper-level entries by virtual-address prefix, letting walks skip to the lowest cached level. They already cut native walks from 4 accesses to about 1.5 (2.5 for random-access workloads) and virtualized walks from 24 to 4.4. Any new proposal must beat this, not the naive 4 — a bar several prior papers measured themselves without.
- POM_TLB / CSALT. A large set-associative TLB in DRAM, with cache prioritization for its entries. Avoids OS changes, but adds a shared cross-VM structure with interference and security questions — and CSALT, tuned for large pages and very frequent context switches, delivers just +0.3% here.
- ASAP. Prefetch the lower table levels while the upper ones are accessed. But PWCs already skip the upper levels, leaving little to overlap (+1.7%), and prefetching without pointer chasing requires the kernel to lay table nodes out contiguously — a guarantee fragmented systems cannot make.
- Elastic Cuckoo Hashing. Replace the radix tree with hash tables: no pointer chasing at all. But each lookup issues three to four parallel accesses (against flattening’s single access), costing net performance (−5.9%) and 32% more cache energy — and growing the hash tables requires large contiguous allocations, again without a fallback.
- The pattern. Designs that require contiguity fail exactly when systems are busiest; designs that bolt on new structures fight the PWC instead of building on it.
How it works
Two knobs: access count, and access latency
-
Conventional 4-level walk + PWC
CR3/TTBR points to the L4 node; 9-bit address slices index L4 → L3 → L2 → L1. The PWC caches upper-level entries by address prefix, so typical walks skip to L2 or L1 directly: ~1.5 accesses on average, but the leaf access almost always remains — and often misses the data caches.
-
Flattening: merge level pairs into 2 MB nodes new
The radix structure makes merging natural: an L2 node points to 512 L1 nodes, so one 2 MB node holds all 512 × 512 = 262,144 leaf entries, indexed by 18 address bits at once. Flatten L4+L3 and L2+L1 and the tree is two levels deep. The L3 PWC now matches 18-bit prefixes, skipping the root: ONE memory access per walk. An 8 GB app's table goes from 4,106 × 4 KB pages to nine 2 MB pages.
-
Opportunism and graceful fallback new
Flattening is a per-node choice marked by one bit in the parent entry (two bits in CR3 for the root). If the kernel cannot get a 2 MB page, it allocates the conventional two 4 KB levels for that subtree — no compaction required, merge later if desired. The stress test motivating this: 0.5% of 2 MB allocations fail at 6% memory oversubscription, 12% at 50%. Large 2 MB DATA pages conflict with flattened L2+L1 (they would need 512 replicated entries), so 1 GB regions holding 32+ large pages simply skip L2+L1 flattening.
-
Recursive-table compatibility (the glue table) new
Windows-style self-referencing tables walk THROUGH themselves to read their own nodes — but 18-bit indexing into a flattened root overshoots the address bits. Fix: one 4 KB sub-table inside the 2 MB root (the "L4*") acts as a traditional L4 holding pointers to all 512 of its sibling sub-tables, restoring every recursion pattern — and letting flattening-unaware hardware walk the table too.
-
Cache prioritization during TLB-miss storms new
Hardware counters detect phases of high TLB miss rate; during them, L2/LLC replacement evicts data in preference to page-table entries 99% of the time (pure LRU otherwise). One tag bit per line — under 0.2% of cache area, and server chips with partition IDs (Arm MPAM) already have the plumbing. The bet: in these phases data misses 95% in L2 anyway, and the page table (16 MB for a dense 8 GB footprint) is 1/512th the data's size — small enough to actually keep.
-
Virtualized: flatten both dimensions new
Guest flattening shrinks the big (11 MB for graph500) guest table and cuts guest-level accesses; host flattening mainly speeds the final data translation (the nested TLB and host PWC already serve the guest-table translations well). Both together: the theoretical walk drops from 24 to 8 accesses, and measured effective accesses from 4.4 to 2.8.
The idea
Split the cost into number-of-accesses × latency-per-access, and notice different tools fit each factor. Flattening is an access-count weapon: the radix tree’s depth is a design choice from an era of small memories, and merging levels just spends a little of today’s abundant capacity (2 MB nodes instead of 4 KB) to halve it. Prioritization is a latency weapon: it cannot make walks shorter, but it can make the one remaining access hit in SRAM instead of DRAM. The factorization also explains the headline asymmetry — natively, PWCs already crushed the access count (1.5), so flattening alone adds just 2.3% and the latency lever dominates (6.8%); under virtualization the 2D walk re-inflates the access count (4.4), and flattening’s share triples (7.1%). Knowing which factor is currently binding is the analysis; the mechanisms are almost mundane.
Opportunistic beats guaranteed. The deep deployability insight is about allocation: any translation scheme that requires large contiguous physical memory (ECH’s hash regions, ASAP’s sequential layouts) inherits a failure mode the kernel cannot paper over — and the paper’s own stress test quantifies it at up to 12% allocation failures. Because flattening exploits the radix structure, every 2 MB node has an exact 4 KB-pair equivalent, so the kernel flattens when it can and shrugs when it cannot, per subtree, at runtime. The same flexibility handles large data pages (selectively un-flatten those regions) and even devices that have never heard of flattening (walk through the glue table). Graceful degradation is not a bolt-on here; it is why the design can exist in a real kernel at +614 lines.
Spend cache on metadata exactly when data caching is failing. Naively, reserving LLC space for page tables robs the application. The paper’s counter-observation: phases of high TLB miss rate are phases of high data miss rate — the same poor locality causes both. In those phases the data was not going to hit anyway (95% L2 miss), while the page table is 512× smaller than the data and therefore the only thing with genuine reuse. The bias costs 4.7 points of extra L2 data misses and saves 36.2 points of walk misses — an easy trade, gated by hardware counters so well-behaved phases pay nothing. A quick real-machine hack (a helper thread touching a graph benchmark’s page table to keep it LLC-resident on an i7) already yields 5%, grounding the simulated 6.8%.
One TLB miss, before and after
graph500 chases a random edge
L4…L1 = page-table levels, root to leaf. PWC = page walker cache (matches virtual-address prefixes to skip levels). Numbers from the paper's gem5 evaluation, 0% large pages.
Step 1 of 6
Baseline: the PWC does its best
The load's virtual address misses both TLBs. The PWC matches the top 27 bits — this 2 MB region was walked recently — so the walk skips L4, L3, and L2 entirely and needs only the L1 leaf entry. One memory access: the PWC has already made walks far cheaper than the naive four.
baseline average: ~1.5 accesses per walk (2.5 for random-access patterns) the research bar is beating THIS, not the naive 4-access walk
Why it works
- A radix tree’s depth is spent one 9-bit slice at a time → merging two levels consumes 18 bits per step using nothing but a bigger node → depth halves, and the existing L3 PWC (now matching 18-bit prefixes) eliminates the root access, leaving exactly one.
- Every flattened node has an exact conventional equivalent (2 MB node ↔ one L2 node + 512 L1 nodes) → the kernel can decide per subtree, at allocation time, with one bit in the parent entry → no guaranteed-contiguity assumption, so fragmentation degrades performance smoothly instead of breaking the design.
- High TLB miss rate and high data miss rate share a cause (poor locality over a large footprint) → during exactly those phases, cache space moved from data to page table costs little (the data was missing) and pays much (the table, 1/512th the size, actually fits and reuses) → walk latency falls from 50.9 to 29.1 cycles.
- Virtualization multiplies walk accesses (each guest level needs host translation) → shrinking either table shrinks the product, and the guest table is the large, poorly-cached factor → flattening gains triple under virtualization (2.3% → 7.1%), and the techniques stack to +14.0%.
- Fewer memory-system accesses and more SRAM hits are also energy wins → −8.7% dynamic cache and −4.7% DRAM energy virtualized — no new structures to power, unlike DRAM-TLB or hashed-table proposals.
What it costs
- Page-table bloat, bounded. A flattened leaf node allocates all 262,144 entries even if sparse regions would have skipped 4 KB nodes — about 2 MB of extra table for an 8 GB app (18 MB vs 16 MB). Negligible against the data, but real for tiny processes, which is why flattening is per-table opportunistic rather than default.
- Large data pages need care. Flattened L2+L1 cannot express a 2 MB data mapping directly (512 replicated entries otherwise); the fix — selectively not flattening 1 GB regions with 32+ large pages — works but adds a heuristic threshold the kernel must manage.
- Prioritization steals from data, deliberately. The 4.7-point rise in L2 data misses is a fine trade during TLB-miss storms, but the mechanism leans on phase detection; in mixed workloads, shared caches need the per-context partition tags to avoid cross-process theft. At extreme scale (4 TB footprints, 0.2% of the table cacheable) the bet may stop paying — the authors say so.
- Simulation-heavy evidence. Performance numbers come from gem5 system-call-emulation mode (no OS interference modeled) plus an industrial simulator; the real-hardware signals are indirect (the helper-thread experiment, the kernel stress test).
- Recursion needs the glue table. Self-referencing tables (Windows) require the embedded L4* sub-table and a walker tweak — small, but a real compatibility surface beyond Linux.
The evaluation
Two simulators (academic gem5 with a faithful Skylake-style PWC, plus an industrial Arm mobile-core simulator), a real kernel prototype stress-tested under memory pressure, three fragmentation scenarios (0%, 50%, 100% large pages), native and virtualized runs, multicore mixes, and head-to-head reimplementations of three prior systems.
Ways to cheapen a page walk
| Property | 4-level table + PWC (the strong baseline) | Restructuring proposals (POM_TLB/CSALT, ASAP, ECH) | Flattening + prioritization (FPT+PTP) |
|---|---|---|---|
| Memory accesses per native walk | |||
| Native performance (0% large pages) | Reference; walk latency 50.9 cycles | ||
| Virtualized (2D walk) | 4.4 effective accesses (24 naive) with dual PWCs + nested TLB | Not their target regime (POM_TLB shared across VMs raises isolation questions) | |
| Behavior when big allocations fail | No large allocations needed | ||
| Deployment surface | Shipping today | New DRAM structures, kernel allocators with guarantees, or hash-resize machinery |
Check yourself
Check your understanding
1. Flattening cuts native page-walk accesses from ~1.5 to 1.0 — yet improves native performance only 2.3%, while under virtualization it delivers 7.1%. Predict the mechanism behind the asymmetry.
2. Deliberately evicting application data to keep page-table entries cached sounds like robbing the workload. Why does it win 6.8%, and what gates it from doing harm?
3. Elastic Cuckoo Hashing eliminates pointer chasing entirely, yet loses 5.9% here while simple flattening wins. What buried assumptions did the comparison expose?
4. Windows walks page tables through themselves — a self-referencing entry makes ordinary page walks return table nodes instead of data. Why does a flattened root break this, and how does the glue table restore it?
Sources
- ACM DL: Every Walk’s a Hit: Making Page Walks Single-Access Cache Hits — ASPLOS 2022 (open access)