使用valgrind测量缓存未命中

use*_*112 6 c++ cpu optimization performance valgrind

我有一个关键路径,它在一个线程中执行,固定到一个核心.

我有兴趣确定缓存未命中的位置.环顾四周之后,valgrind的cachegrind工具似乎对我有所帮助.但是,我对此方案中的工具功能有一些疑问:

  1. 提供缓存未命中的位置有多具体?它输出变量名吗?
  2. 我可以只分析一个线程吗?
  3. 是否可以分析代码的特定部分?
  4. 测量缓存未命中的所有功能是否同样适用于TLB未命中?
  5. 我可以将cachegrind与我的发布/优化代码一起使用吗?
  6. 我理解valgrind使用虚拟机进行采样.这种方法有多准确?

问题1是最重要的.

任何有关命令行参数的帮助都是最受欢迎的.

ser*_*lle 5

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;
}
Run Code Online (Sandbox Code Playgroud)

编译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%  )
Run Code Online (Sandbox Code Playgroud)

I1 未命中率告诉我们没有指令缓存未命中。

D1 未命中率告诉我们有很多缓存 L1 未命中

LL 未命中率告诉我们存在一些L ast L 级缓存未命中。

为了更准确地查看未命中位置,我们可以在应用程序代码中运行kcachegrind cachegrind.out.11549、选择L1 Data Read miss和导航,如下所示这个截图

这应该回答 1)。我认为 2) 3) 和 4) 的答案是否定的。如果您使用调试信息进行编译(没有它们,您将获得全局信息,但不会获得每行信息),则是 5)。从 6 开始,我会说valgrind通常会提供一个非常不错的第一近似值。Goig toperf显然更准确!


Pet*_*des 0

cachegrind 并不是读取 CPU 性能计数器的唯一方法。Linuxperf和 Intel VTune 被广泛使用,并且存在各种其他前端。

我没有使用过cachegrind,只是perf,但它的输出看起来很像使用性能计数器来记录缓存未命中以及导致它们的指令。

提供的缓存未命中位置有多具体?它输出变量名吗?

通过机器代码地址,即特定指令。不同的前端会更好或更差地帮助您弄清楚加载或存储实际上在做什么,而无需读取 asm 并找出 CPU 在哪些寄存器中拥有哪些指针。(他们可以记录计数器触发时正在使用的地址,以识别哪个缓存行。)

从指令地址映射回 C++ 源代码行并不总是显而易见的,如果编译器进行了一些严重的循环转换,或者在每次调用时内联一个计算常量表达式的函数后将常量表达式从循环中拉出。一般来说,我建议分析优化的代码。如果您只是寻找缓存未命中,则优化代码将保留在寄存器中的用于局部变量的额外堆栈可能不会从缓存中逐出许多行,并且每次使用后将变量加载/存储到内存只会触及缓存行已经很热了。不过,如果您总体上查看分析输出,那么注意到 CPU 时间热点作为过程的一部分仅在优化代码中有用。这是特别是。对于 C++ 来说确实如此,其中大量的模板代码预计会内联并优化掉。

是否可以分析代码的特定部分?

是的,应该可以在性能计数器触发时检查调用链,并且仅在当前函数最终由您感兴趣的函数之一调用时才对其进行计数。或者,在输入感兴趣的函数时启用计数。我不知道cachegrind 是否有一个很好的方法来做到这一点。

用于测量缓存未命中的所有功能是否同样适用于 TLB 未命中?

是的,有用于 TLB 未命中和页面遍历的硬件性能计数器,以及时钟周期、微指令退休等。

英特尔 Sandybridge 有 11 个 PMU 计数器,因此您可以在一次运行中完全准确地采样 11 个不同的事物(即无需分时计数器)。