是啥导致我的代码中的缓存未命中?

Posted

技术标签:

【中文标题】是啥导致我的代码中的缓存未命中?【英文标题】:What is causing the cache misses in my code?是什么导致我的代码中的缓存未命中? 【发布时间】:2014-12-18 09:19:15 【问题描述】:

我正在尝试优化在并行区域 (OpenMP) 中调用的部分代码。我使用 Intel VTune Amplifier 2015 进行了内存访问分析,对结果有点困惑。我使用 Intel Composer 2015 重复了优化级别 O1、O2 和 O3 的分析,但结果是相同的。 Amplifier 声称,大多数 LLC 未命中出现在以下三行中:

__attribute__ ((aligned(64)))    double       x[4] = 1.e0,-1.e0, 0.e0, 0.e0;
__attribute__ ((aligned(64)))    double       y[4] = 0.e0,-1.e0, 1.e0, 0.e0;
__attribute__ ((aligned(64)))    double       z[4] = 0.e0, 0.e0,-1.e0, 1.e0;

数据是对齐的,因为它稍后在矢量化代码中被访问。我不能在这里发布整个代码,因为它有版权。这大约是该函数中总缓存未命中的 75%,尽管后面的代码中有大量计算和其他数组。 对于 O0 优化,我得到了更真实的结果,因为那里的行像

res[a] += tempres[start + b] * fact;

但是整个执行过程需要更多的时间(这很清楚)。但我可以相信哪些结果?或者我可以使用哪些替代软件进行测试。

提前致谢!

【问题讨论】:

如果您想验证结果,cachegrind 是另一个工具。 每次执行函数会导致多少缓存未命中?该工具是否会告诉您这些是 i-cache 还是 d-cache 未命中? 这个函数是在一个紧密的循环中调用的吗? 硬件事件可能归因于至少 +-1 指令偏移/错误。采样硬件事件 - 引入更多的不准确性。而且即使你提高了很多准确率,你仍然很难得到准确的答案,因为它是乱序架构,这意味着你“坐等”执行端口的时刻与“发布”的时刻不同“ 操作说明。并且 -O2/-O3 编译后的二进制文件的调试信息不​​准确也存在问题,这也可能导致在映射指令、源代码行和硬件事件时产生很大的混淆。 因此,如果您真的想找出根本原因,请尝试在 VTune 中探索汇编,以确保源代码行和指令(调试信息)之间的相关性符合您的预期。并注意绝对缓存未命中数(如建议的那样),以了解抽样的统计影响。 【参考方案1】:

只看百分比可能会产生误导(100 的 75% 小于 1000 的 10%) - 比较时,您需要查看未命中的绝对数量。

缓存行为也很难凭直觉判断,尤其是与编译器优化和 CPU 管道结合使用时。 看起来优化的构建大多在初始化时错过了缓存(这并不奇怪),但设法将几乎整个计算保持在缓存中,所以我在这里看不到问题。

如果您想确定,则需要研究生成的程序集和硬件参考手册。

寻找一个能确认你期望的工具在很大程度上是浪费时间,因为你不能确定那个工具是不是那个错误的工具。

【讨论】:

感谢 molbdnilo。这部分代码的绝对值也有最多的缓存缺失。我必须计算函数被调用的频率并再次比较。 @user3572032 当然有 - 您应该使用相同的输入比较优化和未优化构建之间的绝对值。

以上是关于是啥导致我的代码中的缓存未命中?的主要内容,如果未能解决你的问题,请参考以下文章

对具有“易失性”属性的动态分配变量的内存访问是不是会导致每次访问的缓存未命中?

“干净数据缓存未命中”和“脏数据缓存未命中”之间的区别

无法理解“perf”返回的关于缓存未命中的指标

当请求中存在授权标头时,它总是一个缓存未命中

总缓存未命中数少于数据缓存未命中数 (PAPI_L1_DCM > PAPI_L1_TCM)

什么被认为是缓存中的高未命中率/低命中率?