【问题标题】:Is it possible to know the address of a cache miss?是否有可能知道缓存未命中的地址?
【发布时间】:2014-07-07 08:51:43
【问题描述】:

每当发生高速缓存未命中时,是否有可能知道该未命中高速缓存行的地址?现代处理器中是否有任何硬件性能计数器可以提供此类信息?

【问题讨论】:

  • MetallicPriest,您可以从使用 vallgrind 的工具 cachegrind 模拟缓存层次结构开始 - valgrind.org/docs/manual/cg-manual.html
  • 还有一个项目来分析 Pentium4 PEBS 的数据地址 - lifl.fr/west/courses/cshp/doc/profguide/node10.html = sourceforge.jp/projects/hardmeter + ADAPTOR
  • 您当然可以使用性能计数器获取导致缓存未命中的指令的地址。所以在 Linux 上你可以perf record -e L1-dcache-loads ./a.out 然后perf report -Mintel。通常你知道一条指令正在访问哪个数组,所以在很多情况下这已经足够了。 (也有其他级别缓存的计数器。获取ocperf.py 并使用ocperf.py list 获取支持的全套硬件计数器)。
  • @PeterCordes - PMU 还提供最近实现的数据地址,作为其“内存 PEBS”事件的一部分。它还告诉您访问是否是 L1 命中、LFB 命中、是否被锁定、TLB 行为是什么以及许多其他好东西!这在 Linux 上作为perf mem 的一部分公开。

标签: caching x86-64 processor


【解决方案1】:

是的,在现代英特尔硬件上,精确的内存采样事件不仅可以跟踪指令的地址,还可以跟踪数据地址。这些事件还包括大量其他信息,例如满足内存访问的缓存层次结构的级别、总延迟等。

您可以使用perf mem 对这些信息进行抽样并生成报告。

例如下面的程序:

#include <stddef.h>

#define SIZE (100 * 1024 * 1024)

int p[SIZE] = {1};

void do_writes(volatile int *p) {
    for (size_t i = 0; i < SIZE; i += 5) {
        p[i] = 42;
    }
}

void do_reads(volatile int *p) {
    volatile int sink;
    for (size_t i = 0; i < SIZE; i += 5) {
        sink = p[i];
    }
}

int main(int argc, char **argv) {
    do_writes(p);
    do_reads(p);
}

编译:

g++  -g -O1 -march=native   perf-mem-test.cpp   -o perf-mem-test

并运行:

sudo perf mem record -U ./perf-mem-test && sudo perf mem report

生成按延迟排序的内存访问报告,如下所示:

Data Symbol 列显示负载所针对的地址 - 这里的大多数显示为 p+0xa0658b4 之类的东西,这意味着距离 p 开头的偏移量为 0xa0658b4,这在代码读取时是有意义的并写p。该列表按“本地权重”排序,这是参考周期中的访问延迟1

请注意,记录的信息只是内存访问的样本:记录每次未命中通常会包含太多信息。此外,默认情况下它只记录延迟为 30 个周期或更长时间的负载,但您显然可以使用命令行参数进行调整。

如果您只对在所有级别的缓存中未命中的访问感兴趣,那么您正在寻找“本地 RAM 命中”行2。也许您可以将采样限制为仅缓存未命中 - 我很确定英特尔内存采样支持这一点,我认为您可以告诉 perf mem 只查看未命中。

最后,请注意,这里我在record 之后使用-U 参数,它指示perf mem 仅记录用户空间事件。默认情况下,它将包括内核事件,这可能对您有用,也可能没用。对于示例程序,有许多与将p 数组从二进制文件复制到可写进程内存相关的内核事件。

请记住,我专门安排了我的程序,使得全局数组 p 最终出现在初始化的 .data 部分(二进制文件约为 400 MB!),因此它在清单。大多数情况下,您的进程将访问动态分配的内存或堆栈内存,这只会给您一个原始地址。是否可以将其映射回有意义的对象取决于您是否跟踪了足够的信息以使其成为可能。


1认为它在引用周期中,但我可能错了,内核可能已经将它转换为纳秒?

2 这里的“本地”和“命中”部分是指我们命中了附加到当前内核的 RAM,即我们没有去与另一个内核关联的 RAM多套接字 NUMA 配置中的套接字。

【讨论】:

  • 您应该提到 PEBS = 基于事件的精确采样。我没有找到关于 PEBS 本身的好 URL,只是提到它作为特定分析器使用的东西的页面。
  • 奇怪的是,我只是在寻找一个特定于 PEBS 内存分析的好链接,但我没有找到。 SDM 当然会详细介绍它。
  • @PeterCordes - 我放弃了寻找好的链接,只是删除了 PEBS。
  • 您错过了第二次出现。第一次使用时只需写“PEBS(基于事件的精确采样)”即可。
【解决方案2】:

如果您想知道特定处理器上每个缓存未命中的确切虚拟或物理地址,那将非常困难,有时甚至是不可能的。但是您更有可能对昂贵的内存访问模式感兴趣;那些由于错过了缓存子系统的一个或多个级别而导致大延迟的模式。请注意,重要的是要记住,一个处理器上的缓存未命中可能是另一个处理器上的缓存命中,这取决于每个处理器的设计细节以及操作系统。

有几种方法可以找到这样的模式,常用的有两种。一种是使用模拟器,例如gem5Sniper。另一种是使用硬件性能事件。表示缓存未命中的事件可用,但它们不提供有关未命中原因或发生位置的任何详细信息。但是,使用分析器,您可以将相应硬件性能事件报告的缓存未命中与导致它们的指令大致关联起来,而这些指令又可以使用调试信息映射回源代码中的位置。此类分析器的示例包括Intel VTune AmplifierAMD CodeXL。模拟器和分析器产生的结果可能不准确,因此在解释它们时必须小心。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多