我有一个关键路径,它在一个线程中执行,固定到一个核心。
我有兴趣确定发生缓存未命中的位置。环顾四周后,valgrind 的 cachegrind 工具似乎对我有帮助。但是,我对该工具在这种情况下的功能有一些疑问:
- 提供的缓存未命中位置有多具体?是否输出变量名?
- 我可以只介绍一个话题吗?
- 是否可以分析代码的特定部分?
- 所有用于测量缓存未命中的功能,它们是否同样适用于 TLB 未命中?
- 我可以将 cachegrind 与我的发布/优化代码一起使用吗?
- 我知道 valgrind 使用虚拟机进行采样。这种方法的准确性如何?
问题 1 是最重要的。
非常感谢任何有关命令行参数的帮助。
cachegrind
可以输出有关缓存未命中的全局和局部信息,并在行级别进行注释(如果原始程序是使用调试信息编译的)。比如下面的代码:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
int main(int argc, char**argv) {
size_t n = (argc == 2 ) ? atoi(argv[1]) : 100;
double* v = malloc(sizeof(double) * n);
for(size_t i = 0; i < n ; i++)
v[i] = i;
double s = 0;
for(size_t i = 0; i < n ; ++i)
s += v[i] * v[n - 1 - i];
printf("%ld\n", s);
free(v);
return 0;
}
用 gcc a.c -O2 -g -o a 编译
并使用 valgrind --tool=cachegrind ./a 10000000
输出运行:
==11551== Cachegrind, a cache and branch-prediction profiler
==11551== Copyright (C) 2002-2013, and GNU GPL'd, by Nicholas Nethercote et al.
==11551== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11551== Command: ./a 10000000
==11551==
--11551-- warning: L3 cache found, using its data for the LL simulation.
80003072
==11551==
==11551== I refs: 150,166,282
==11551== I1 misses: 876
==11551== LLi misses: 870
==11551== I1 miss rate: 0.00%
==11551== LLi miss rate: 0.00%
==11551==
==11551== D refs: 30,055,919 (20,041,763 rd + 10,014,156 wr)
==11551== D1 misses: 3,752,224 ( 2,501,671 rd + 1,250,553 wr)
==11551== LLd misses: 3,654,291 ( 2,403,770 rd + 1,250,521 wr)
==11551== D1 miss rate: 12.4% ( 12.4% + 12.4% )
==11551== LLd miss rate: 12.1% ( 11.9% + 12.4% )
==11551==
==11551== LL refs: 3,753,100 ( 2,502,547 rd + 1,250,553 wr)
==11551== LL misses: 3,655,161 ( 2,404,640 rd + 1,250,521 wr)
==11551== LL miss rate: 2.0% ( 1.4% + 12.4% )
I1 未命中率告诉我们没有指令缓存未命中。
D1 未命中率告诉我们有很多缓存 L1 未命中
LL 未命中率告诉我们有一些 LL级缓存未命中。
为了更准确地查看未命中位置,我们可以运行 kcachegrind cachegrind.out.11549
,选择 L1 Data Read miss
并在应用程序代码中导航, 如图所示
这应该回答 1)。我认为答案是否定的 2) 3) 和 4)。如果您使用调试信息进行编译(没有它们,您将获得全局信息,但不是每行信息),则 5) 是肯定的。从 6) 开始,我会说 valgrind
通常会提供非常不错的一阶近似值。 Goig 到 perf
显然更准确!