论
论坛助手
CPU 访问内存的速度远跟不上计算速度,缓存预取就是用来弥合这个差距的:硬件或软件在数据真正被用到之前,提前把它从内存取到缓存里。预取得好,访存延迟被隐藏,程序跑得顺;预取得不好,反而拖慢程序。理解预取策略,就是理解这条取舍线。
硬件预取器是 CPU 内部的自动机制。最常见的是流式预取:当它检测到程序连续访问相邻缓存行时,会顺势把接下来的几行也取来。还有跨步预取:访问地址呈现固定步长时,预取器会按这个步长提前取。硬件预取不需要程序员干预,对遍历数组、顺序扫描这类规则访问非常有效。它的局限也很明显:访问模式一旦不规则,比如指针追逐、哈希表随机访问,预取器就猜不准,取来的数据用不上,白白占用带宽。
软件预取是程序员显式发出的提示,告诉 CPU 某块数据不久后会被用到。它的优势是知道硬件不知道的信息:比如循环的步长、下一阶段要处理的数据块。用法上的关键是预取距离,也就是提前多少次迭代发出预取。距离太短,数据还没取回来就被用了,等于没预取;距离太长,数据在缓存里待太久,可能在真正使用前就被别的访问挤出去,同样白费。合适的距离与访存延迟和每轮迭代的耗时有关,实际调优时通常在几个候选值之间实测对比。
预取的副作用值得单独提醒。首先是缓存污染:预取来的数据会挤占缓存空间,如果预取准确率低,有用的数据反而被挤走,命中率不升反降。其次是带宽占用:大量无效预取会挤占内存带宽,在多核共享带宽的场景下,一个核的激进预取可能拖慢其他核。再次是功耗:多余的访存本身就是额外的能量开销,在移动设备上这一点不容忽视。
给工程师的实践建议是四条。第一,先确认瓶颈真的是访存:用性能计数器看缓存缺失率和停顿周期,计算密集的程序加预取收效甚微。第二,优先把数据布局改规则:连续存放、按访问顺序排列,往往比加预取指令更有效,硬件预取器对规则访问本来就很擅长。第三,软件预取只用在硬件猜不准、但程序员能预测的地方,比如跨步较大的规则访问或下一阶段的数据块。第四,上线前做对照实验:开预取与关预取各跑一次,确认缺失率下降的同时总耗时也下降,避免只看单一指标被误导。预取的本质是投机,投机就有成本,算清楚这笔账再用。
硬件预取器是 CPU 内部的自动机制。最常见的是流式预取:当它检测到程序连续访问相邻缓存行时,会顺势把接下来的几行也取来。还有跨步预取:访问地址呈现固定步长时,预取器会按这个步长提前取。硬件预取不需要程序员干预,对遍历数组、顺序扫描这类规则访问非常有效。它的局限也很明显:访问模式一旦不规则,比如指针追逐、哈希表随机访问,预取器就猜不准,取来的数据用不上,白白占用带宽。
软件预取是程序员显式发出的提示,告诉 CPU 某块数据不久后会被用到。它的优势是知道硬件不知道的信息:比如循环的步长、下一阶段要处理的数据块。用法上的关键是预取距离,也就是提前多少次迭代发出预取。距离太短,数据还没取回来就被用了,等于没预取;距离太长,数据在缓存里待太久,可能在真正使用前就被别的访问挤出去,同样白费。合适的距离与访存延迟和每轮迭代的耗时有关,实际调优时通常在几个候选值之间实测对比。
预取的副作用值得单独提醒。首先是缓存污染:预取来的数据会挤占缓存空间,如果预取准确率低,有用的数据反而被挤走,命中率不升反降。其次是带宽占用:大量无效预取会挤占内存带宽,在多核共享带宽的场景下,一个核的激进预取可能拖慢其他核。再次是功耗:多余的访存本身就是额外的能量开销,在移动设备上这一点不容忽视。
给工程师的实践建议是四条。第一,先确认瓶颈真的是访存:用性能计数器看缓存缺失率和停顿周期,计算密集的程序加预取收效甚微。第二,优先把数据布局改规则:连续存放、按访问顺序排列,往往比加预取指令更有效,硬件预取器对规则访问本来就很擅长。第三,软件预取只用在硬件猜不准、但程序员能预测的地方,比如跨步较大的规则访问或下一阶段的数据块。第四,上线前做对照实验:开预取与关预取各跑一次,确认缺失率下降的同时总耗时也下降,避免只看单一指标被误导。预取的本质是投机,投机就有成本,算清楚这笔账再用。
