Valgrind 模拟一个 Intel x86 处理器,并在该合成处理器中运行我们的测试程序。这两个处理器并不完全相同。Valgrind 被编译成一个共享对象,valgrind.so。一个 shell 脚本valgrind设置LD_PRELOAD环境变量,使其指向 valgrind.so。这使得 .so 文件作为额外的库加载到任何后续执行的动态链接 ELF 二进制文件中,从而允许程序被调试。
动态链接器调用 Valgrind 的初始化函数。然后合成 CPU 从真实 CPU 接管控制权。在内存中可能还有其他 .so 文件。动态链接器调用所有这些 .so 文件的初始化函数。现在动态链接器调用main已加载程序的 main 函数。当 main 函数返回时,合成 CPU 调用 valgrind.so 的终结函数。在终结函数执行期间,会打印所有检测到的错误的摘要,并检查内存泄漏。终结函数退出,将控制权从合成 CPU 交还给真实的 CPU。
现代 x86 机器使用两级缓存。这两级缓存是 L1 和 L2,其中 L1 是一个分离缓存,由指令缓存 (I1) 和数据缓存 (D1) 组成。L2 是一个统一缓存。
缓存的配置意味着它的大小、关联性和行数。如果处理器请求的数据出现在较高层级中,则称为命中。如果在较高层级中未找到数据,则该请求称为未命中。然后访问层级结构中的较低层级以检索包含请求数据的块。在现代机器中,首先在 L1 中搜索处理器请求的数据/指令。如果命中,则将该数据/指令复制到处理器中的某个寄存器。否则,搜索 L2。如果命中,则将数据/指令复制到 L1,然后从 L1 复制到寄存器。如果对 L2 的请求也是未命中,则必须访问主内存。
Valgrind 可以模拟缓存,这意味着它可以显示程序运行时缓存中发生的事情。为此,首先使用以下选项编译您的程序-g选项,像往常一样。然后使用 shell 脚本cachegrind而不是valgrind.
示例输出
==7436== I1 refs: 12,841
==7436== I1 misses: 238
==7436== L2i misses: 237
==7436== I1 miss rate: 1.85%
==7436== L2i miss rate: 1.84%
==7436==
==7436== D refs: 5,914 (4,626 rd + 1,288 wr)
==7436== D1 misses: 357 ( 324 rd + 33 wr)
==7436== L2d misses: 352 ( 319 rd + 33 wr)
==7436== D1 miss rate: 6.0% ( 7.0% + 2.5% )
==7436== L2d miss rate: 5.9% ( 6.8% + 2.5% )
==7436==
==7436== L2 refs: 595 ( 562 rd + 33 wr)
==7436== L2 misses: 589 ( 556 rd + 33 wr)
==7436== L2 miss rate: 3.1% ( 3.1% + 2.5% )
|
L2i misses means the number of instruction misses that occur in L2
cache.
L2d misses means the number of data misses that occur in L2 cache.
Total number of data references = Number of reads + Number of writes.
Miss rate means fraction of misses that are not found in the upper
level.
|
shell 脚本cachegrind还会生成一个文件,cachegrind.out,其中包含逐行缓存分析信息,这些信息对于人类来说是难以理解的。一个程序vg_annotate可以轻松地解释这些信息。如果 shell 脚本vg_annotate在不带任何参数的情况下使用,它将读取文件cachegrind.out并生成人类可理解的输出。
当 C、C++ 或汇编源代码程序作为输入传递给vg_annotate时,它会显示缓存读取、写入、未命中等的次数。
I1 cache: 16384 B, 32 B, 4-way associative
D1 cache: 16384 B, 32 B, 4-way associative
L2 cache: 262144 B, 32 B, 8-way associative
Command: ./a.out
Events recorded: Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
Events shown: Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
Event sort order: Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
Thresholds: 99 0 0 0 0 0 0 0 0
Include dirs:
User annotated: valg_flo.c
Auto-annotation: off
|
用户注释的源代码valg_flo.c:
Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
. . . . . . . . . #include<stdlib.h>
. . . . . . . . . int main()
3 1 1 . . . 1 0 0 {
. . . . . . . . . float *p, *a;
6 1 1 . . . 3 0 0 p = malloc(10*sizeof(float));
6 0 0 . . . 3 0 0 a = malloc(10*sizeof(float));
6 1 1 3 1 1 1 1 1 a[3] = p[3];
4 0 0 1 0 0 1 0 0 free(a);
4 0 0 1 0 0 1 0 0 free(p);
2 0 0 2 0 0 . . . }
|