分析例程的缓存未命中

Posted

技术标签:

【中文标题】分析例程的缓存未命中【英文标题】:Profiling cache misses of a routine 【发布时间】:2014-06-12 04:50:23 【问题描述】:

我想分析 C++ 程序中特定例程的缓存未命中(率)。我知道一些分析工具,但它们似乎不太让我满意。

据我所知:

gprof 可以生成调用图和代码覆盖率,但不包括缓存未命中分析。

valgrind (cachegrind) 可以分析缓存未命中,但似乎只适用于整个应用程序。

oprofile 确实具有基于符号的输出。但我有点困惑:假设过程 A() 调用 B()(可能是库或系统调用),并且 B() 内部发生缓存未命中,是否归因于 A()?毕竟我想在 A() 的整个执行过程中计算缓存未命中。

最后一个问题。 OProfile 需要为每个新会话重新启动守护程序。假设我想通过一系列不同的参数和输入来分析我的程序。我怎样才能以自动化的方式做到这一点?有没有办法像在代码中插入 gettimeofday() 来获取缓存统计信息?

【问题讨论】:

【参考方案1】:

假设您在特定函数中的缓存未命中很重要,那么使用 cachegrind 或 oprofile 应该可以正常工作。如果您在该函数中的缓存未命中对于程序的整个执行而言并不重要,那么您为什么要打扰呢?

根据记忆,我使用 oprofile 已经有几年了,但您不需要重新启动守护程序,只需使用“重置我的数据”(opcontrol --reset)。由于 oprofile 是系统范围的,您可以简单地运行带有多个测试用例的脚本 - oprofile 将按进程拆分数据,但如果您在任何特定功能中缓存使用率很高,则应该显示在缓存未命中列表中。如果它没有出现,那么你的代码不会在缓存中丢失。

我一直这样做的方式是编写一个执行opcontrol --reset --start event=CPU_CLK_UNHALTED:400000 的脚本,然后是我正在测试的任何程序,然后是opcontrol --stop --save=oprofile.result

请注意,oprofile 是基于统计和样本的,因此您无法测量单个缓存未命中,它会记录缓存未命中计数器达到限制的地址。因此,您可能会遇到这样一种情况,如果您有 1000 次缓存未命中的“限制”,那么您在函数 A 中获得 999 次缓存未命中,然后在函数 B 中出现一次缓存未命中,函数 B 被“记入”缓存未命中。但是,假设您没有非常病态的代码,您将在缓存中未命中的代码区域中获得缓存未命中记录。

【讨论】:

谢谢@Mats。由于我这样做是出于学术研究目的,因此无论如何我都需要描述特定的例程。您提到不可能测量确切的缓存未命中计数,因为 oprofile 是基于样本的。但是通过将样本数乘以“限制”来获得估计值是否合理? Oprofile 将缓存未命中数作为“命中”计数(即代码中该位置发生该测量中断的次数)和总数的百分比(为了方便)。当然,如果您知道限制,您可以计算回缓存未命中的数量。 Cachegrind 将提供更准确的测量,因为它模拟内存访问(但运行速度要慢得多)。如果代码很简单,你总是可以手动“伪装成处理器和缓存”。 我明白了。所以就缓存未命中计数而言,cachegrind 比 oprofile 更准确,因为它是 simulating 而不是 sampling,对吧?但是我好像还是有问题。我的程序是多线程的。我刚刚检查了 cachegrind 的规范,它说它对待线程的方式与通常不同。所以结果可能没有用。是真的吗? 你真的在尝试做一些不可能的事情,不是吗?是的,这使事情复杂化了。 Valgrind,如果我理解正确的话,会在某些条件下序列化线程(一次运行一个线程)。我不确定确切的细节,但它确实改变了行为。你确定你已经正确理解了这个任务吗,因为对我来说这听起来“不太可能被解决”(至少有任何准确性)。

以上是关于分析例程的缓存未命中的主要内容,如果未能解决你的问题,请参考以下文章

代码分析器哪些参数显示缓存未命中?

如何使用 Visual 2013 分析缓存未命中

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

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

Ehcache中的“缓存未命中”和“内存缓存未命中”有啥区别?

MyBatis 缓存机制分析