问题描述
为了测量程序中缓存未命中的影响,我想将缓存未命中引起的延迟对用于实际计算的周期进行计算。
我使用 perf stat
来测量我的程序中的周期、L1 负载、L1 未命中、LLC 负载和 LLC 未命中。这是一个示例输出:
467 769,70 msec task-clock # 1,000 cpus utilized
1 234 063 672 432 cycles # 2,638 GHz (62,50%)
572 761 379 098 instructions # 0,46 insn per cycle (75,00%)
129 143 035 219 branches # 276,083 M/sec (75,00%)
6 457 141 079 branch-misses # 5,00% of all branches (75,00%)
195 360 583 052 L1-dcache-loads # 417,643 M/sec (75,00%)
33 224 066 301 L1-dcache-load-misses # 17,01% of all L1-dcache hits (75,00%)
20 620 655 322 LLC-loads # 44,083 M/sec (50,00%)
6 030 530 728 LLC-load-misses # 29,25% of all LL-cache hits (50,00%)
那么我的问题是: 如何将缓存未命中数转换为“丢失”时钟周期数? 或者,获取数据所花费的时间比例是多少?
我认为构造函数应该知道这个因素。我的处理器是 Intel Core i7-10810U,我无法在 specifications 或此 list 基准 cpu 中找到此信息。
这个 related problem 描述了如何测量缓存未命中丢失的周期数,但有没有办法将其作为硬件信息获取?理想情况下,输出将类似于:
L1-hit: 3 cycles
L2-hit: 10 cycles
LLC-hit: 30 cycles
RAM: 300 cycles
解决方法
乱序执行和内存级并行的存在是为了通过将有用的工作与正在运行的时间数据重叠来隐藏一些延迟。如果您简单地将 L3 未命中数乘以每个 300 个周期,这可能会超过整个程序所用的周期总数。 perf 事件 cycle_activity.stalls_l3_miss
(存在于我的 Skylake CPU 上)应该在没有 uops 执行并且存在未完成的 L3 缓存未命中时计算周期。即执行完全停止时的循环。但是也会有一些工作的循环,但比没有缓存未命中要少,这更难评估。
TL:DR:内存访问被大量流水线化;整个核心不会因一次缓存未命中而停止,这就是重点。指针追踪基准(用于测量延迟)只是最坏的情况,唯一的工作是取消引用加载结果。请参阅 Modern Microprocessors A 90-Minute Guide!,其中有一节介绍了内存和“内存墙”。另请参阅 https://agner.org/optimize/ 和 https://www.realworldtech.com/haswell-cpu/ 以了解有关乱序 exec CPU 的详细信息以及它们如何在一条指令等待来自缓存未命中的数据时继续独立工作,直到其乱序窗口大小的限制。 (https://blog.stuffedcow.net/2013/05/measuring-rob-capacity/)
回复:来自供应商的数字:
L3 和 RAM 延迟不是固定数量的核心时钟周期:首先,核心频率是可变的(并且独立于非核心和内存时钟),其次是因为来自其他核心的争用以及互连上的跳数. (相关:Is cycle count itself reliable on program timing? 讨论了独立于 L3 和内存的核心频率的一些影响)
也就是说,英特尔的优化手册确实包括一张表,其中包含 L1 和 L2 的准确延迟,以及 L3 的典型延迟,以及 Skylake 服务器上的 DRAM。 (2.2.1.3 Skylake Server 微架构缓存推荐) https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html#optimization - 他们说 SKX L3 延迟通常是 50-70 个周期。 DRAM 速度在一定程度上取决于 DIMM 的时序。
其他人已经测试了特定的 CPU,例如 https://www.7-cpu.com/cpu/Skylake.html。