每当发生高速缓存未命中时,是否可以知道该高速缓存未命中行的地址?现代处理器中是否有任何硬件性能计数器可以提供此类信息?
最佳答案
是的,在现代英特尔硬件上,存在精确的内存采样事件,这些事件不仅跟踪指令的地址,还跟踪数据的地址。这些事件还包括大量其他信息,例如满足存储访问的高速缓存层次结构级别,总延迟等。
您可以使用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
之类的东西,这意味着距0xa0658b4
开头p
的偏移量,这在代码正在读写p
时是有意义的。该列表按“本地权重”排序,“本地权重”是引用周期1中的访问延迟。请注意,记录的信息只是内存访问的一个示例:记录每个未命中通常是过多的信息。此外,默认情况下,它仅记录延迟为30个周期或更长的负载,但是您显然可以使用命令行参数对其进行调整。
如果您只对在所有级别的缓存中丢失的访问感兴趣,那么您将在寻找“本地RAM命中”第2行。也许您可以将采样限制为仅缓存未命中-我很确定英特尔内存采样功能支持这一点,而且我认为您可以告诉
perf mem
仅查看未命中。最后,请注意,这里我在
-U
之后使用record
参数,该参数指示perf mem
仅记录用户空间事件。默认情况下,它将包括内核事件,这可能对您有用或可能不有用。对于示例程序,有许多内核事件与将p
数组从二进制文件复制到可写进程内存有关。请记住,我对程序进行了专门安排,使得全局数组
p
最终出现在初始化的.data
节中(二进制文件为〜400 MB!),以便在 list 中显示正确的符号。在大多数情况下,您的进程将要访问动态分配的或堆栈的内存,这只会给您一个原始地址。是否可以将此映射回有意义的对象取决于您是否跟踪足够的信息以使之成为可能。1我认为它处于引用周期,但是我可能错了,内核可能已经将其转换为纳秒了?
2这里的“本地”和“命中”部分是指我们命中了连接到当前内核的RAM,即我们没有转到与多插槽NUMA配置中的另一个插槽关联的RAM。
关于caching - 是否可以知道高速缓存未命中的地址?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23736999/