现代 CPU 能够以极高的速度处理指令,通常每纳秒可执行 4 到 8 次操作。相比之下,从系统内存(DRAM)访问数据的速度明显较慢,耗时在 50 到 100 纳秒之间。这种差异意味着 CPU 在理论上每进行一次 RAM 内存访问,可以执行 400 到 800 条指令。为了缓解这一问题,处理器使用了缓存层级(L1、L2 和 L3),这些缓存在物理上更靠近核心,且速度远快于 DRAM。然而,这些缓存容量相对较小——L1 缓存通常仅为 64KB 左右——这要求软件设计必须具备“缓存友好性”,以确保频繁访问的数据保留在这些高速层级中。

在对大型数据集执行 `GROUP BY` 聚合操作时,幼稚的实现可能会使用单个大型哈希表来存储每个唯一的组键。如果唯一键的数量很大,哈希表的大小将超过 CPU 缓存的容量。这将迫使 CPU 在几乎每次操作中都执行“冷”内存访问,从 DRAM 中获取数据。即使系统监控显示 CPU 利用率为 100%,处理器实际上可能大部分时间都在“停顿”——即等待数据从内存到达,而不是在执行指令。在一次使用 9900 万行数据集的测试中,幼稚的方法导致 CPU 在超过 50% 的时间内因内存而停顿,而仅在约 16% 的时间内完成操作。

为了优化这一点,开发人员可以使用称为基数分区(radix partitioning)的技术。这涉及根据键的哈希值将数据拆分为固定数量的较小分区(例如 4,096 个)。通过一次处理这些较小的分区,相应的哈希表更有可能完全放入 CPU 缓存中。虽然这种方法在技术上需要更多的总操作——因为数据必须在聚合之前移动到分区中——但这种权衡是非常有利的。在同样的测试中,切换到基数分区将内存停顿时间从 50.1% 降低到 23.5%,并将从 DRAM 中提取的数据量减少了 60%,这证明了如果这些指令能够取代缓慢的内存等待,那么增加总指令数往往是一种获胜的策略。