Home · ASPLOS 2022 · Memory Systems & Caches

Every walk's a hit: making page walks single-access cache hits

Chang Hyun Park, Ilias Vougioukas, Andreas Sandberg and David Black-Schaffer

Conference
ASPLOS 2022
Published
2022
Pages
128-141
Citations
34

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

  1. 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.

  2. 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.

  3. 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.

  4. 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
misses, the processor must walk the
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
. Nothing about the data access can proceed meanwhile: the walk sits squarely on the critical path. Graph analytics, hash joins, key-value stores — anything with poor locality over a large footprint — spends a meaningful slice of its cycles in this purely administrative pointer chase, 50.9 cycles per walk on average in the paper’s measurements.

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
. Hardware caching claws most of this back (to 4.4 accesses on average), but the residual is still triple the native cost, at datacenter scale, forever. And the industry’s trajectory makes everything worse: memories keep growing, five-level tables are arriving, and TLBs cannot keep pace. The appeal of this paper is that it attacks the walk itself with mechanisms an OS vendor could actually ship — which is exactly where the cleverer prior proposals stumbled.

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

Kernel prototype: Linux 5.8.13, +614/−109 lines, exploiting Linux's existing level-folding machinery; runs on real hardware via a shim table plus an Arm functional simulator. Evaluation: gem5 with a faithful Skylake-style PWC model (server, 16 MB LLC), plus an industrial Arm mobile-core simulator for robustness.

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

Property4-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 TLBNot their target regime (POM_TLB shared across VMs raises isolation questions)
Behavior when big allocations failNo large allocations needed
Deployment surfaceShipping todayNew DRAM structures, kernel allocators with guarantees, or hash-resize machinery
gem5, 2 GHz OOO x86, 16 MB LLC, Skylake-style TLBs and PWC, DDR4-2400; workloads: GraphBIG, graph500, GUPS, liblinear, hashjoin, XSBench, biobench, SPEC, and a random-access microbenchmark (5–8 GB footprints). Fragmentation stacked as 0%/50%/100% large pages; 50% is the realistic case (combined gain there: +5.8 points vs ASAP 1.2 / ECH −3.2 / CSALT 0.7). Multicore: 4 cores, 32 MB shared L3, 20 mixes — +11.5% combined at 0% LP.Underlined values explain themselves when clicked.

Check yourself

Check your understanding

  1. 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. 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. 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. 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

Related papers on Memory Systems & Caches

See all 118 →