CPU caches (L1, L2, L3) deliver data orders of magnitude faster than DRAM, a gap highlighted by the Pivot Lake benchmark.
*When a database shows a CPU pegged at 100%, the bottleneck is often memory latency, not CPU power. Pivot Lake’s benchmark on a bare‑metal EPYC server proves that half of CPU cycles stall waiting for DRAM.*
A database server flashing a CPU at 100% can be a false alarm. The metric hides a deeper truth: the processor is likely waiting on memory. Pivot Lake’s recent blog post dissected this paradox on a high‑end EPYC server, exposing how memory latency throttles query speed even when the CPU appears saturated. By pinning a single core and measuring perf counters, the team proved that half of the core’s cycles were stalled on DRAM while only a fraction were doing useful work. The experiment centered on a naïve GROUP BY over 99 million rows, a scenario that forces the hash table into main memory. The result is a stark reminder that raw compute power cannot compensate for slow memory access.
A CPU can retire millions of instructions per nanosecond—modern cores manage roughly 4‑8 instructions per ns. A RAM access, however, costs 50‑100 ns. That disparity means a core can execute 400‑800 instructions for every single DRAM fetch. When the core repeatedly hits RAM, it spends most of its cycles waiting, even though the OS reports 100% utilization. The CPU is technically idle during those stalls, a state captured by perf counters as "backend_bound_memory".
Caches sit on‑chip and shrink the access gap. L1 cache delivers data in about 1 ns, a 50‑100× speedup over DRAM. L2 takes 3‑4 ns, L3 10‑15 ns. Their sizes—~64 KB for L1, ~1 MB for L2, and a few hundred MB for L3 on high‑end CPUs—are tiny compared with the dataset. When a query forces the processor to fetch data that does not fit in any cache, each access falls back to DRAM, inflating stall time. The Pivot engine’s naive GROUP BY builds a hash table that grows to millions of entries, guaranteeing cold RAM hits for most lookups.
Running on a CherryServers bare‑metal AMD EPYC 9124 Genoa (16 cores, 128 GB DDR5), the team executed SELECT count(*) FROM (SELECT COUNT(*), UserID FROM hits GROUP BY UserID) on the ClickBench "hits" table (99 m rows, ~17 m unique UserID). The single‑core run took 3.9 seconds on first execution and 3.6 seconds on the second, with no disk I/O because the data sat in page‑cache. Perf stats showed 50.1 % of CPU slots stalled on memory (backend_bound_memory) while only 16.1 % were retiring micro‑ops. The elapsed wall‑clock time recorded by perf was 4.030148989 seconds, confirming that memory stalls dominate the runtime.
Engineers must design queries and data structures that stay within cache limits. Group‑by operations that explode hash tables to millions of keys will inevitably spill to DRAM, throttling performance despite a fully‑utilized CPU. Optimizing for cache locality—processing data in small chunks, pre‑aggregating, or using vectorized execution—can shift work from the memory‑bound path to the retiring path, reducing the 50 % stall fraction observed in the benchmark. The findings underscore that raw CPU cores are not a silver bullet for database speed.
The CPU spends about 50.1% of its slots stalled on memory accesses, so it appears fully utilized even though it is waiting for DRAM.
The dataset contains roughly 17 million unique UserID values.
A bare‑metal server with an AMD EPYC 9124 Genoa CPU, 16 cores, and 128 GB DDR5 memory was used.
The lesson is clear: database performance hinges on memory architecture as much as on core count. Engineers who ignore cache behavior risk building systems that look busy but deliver little throughput. As workloads grow and data sets swell, the pressure on DRAM will only increase, making memory‑aware design a non‑negotiable priority for any high‑performance data platform.
Sources: [Pivot Lake blog post "It's the Memory Latency, Stupid" (https://pivotlake.io/blog/its-the-memory-latency-stupid/)]