我正在尝试了解perf记录的缓存未命中。我有一个最小的程序:
int main(void)
{
return 0;
}
如果我编译此:
gcc -std=c99 -W -Wall -Werror -O3 -S -o test.S test.c
我得到了一个预期很小的程序:
.file "test.c"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 4.7.2-5) 4.7.2"
.section .note.GNU-stack,"",@progbits
仅使用xorl
和ret
这两个指令,程序的大小应小于高速缓存行,因此我希望如果运行perf -e "cache-misses:u" ./test
,则只会看到单个高速缓存未命中。但是,我反而看到2到〜400之间。同样,perf -e "cache-misses" ./test
导致〜700至〜2500。
这是仅是一次估算计数的情况,还是有关高速缓存未命中的方式是否使估算值近似?例如,如果我生成然后读取内存中的整数数组,我是否可以推断预取(顺序访问应允许进行完美的预取)还是在起作用?
您创建了一个main
而不是_start
,并且可能将其构建为动态链接的可执行文件!!因此,这里有所有的CRT启动代码,初始化libc和几个系统调用。运行strace ./test
,查看它正在调用多少个系统。 (当然,用户空间中有很多工作不涉及系统调用)。
更有趣的是一个静态链接的可执行文件,它仅从_exit(0)
入口点使用exit_group(0)
指令进行syscall
或_start
系统调用。
给出具有以下内容的exit.s
:
mov $231, %eax
syscall
将其构建为静态可执行文件,因此这两条指令是在用户空间中执行的唯一指令:
$ gcc -static -nostdlib exit.s
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
# the default is fine, our instructions are at the start of the .text section
$ perf stat -e cache-misses:u ./a.out
Performance counter stats for './a.out':
6 cache-misses:u
0.000345362 seconds time elapsed
0.000382000 seconds user
0.000000000 seconds sys
我告诉它只计算user-space高速缓存未命中,而不是运行进程的核心上的所有内容,只计算cache-misses:u
。 (这包括进入用户空间之前和处理exit_group()
系统调用时内核高速缓存未命中。以及可能的中断处理程序。)>
(PMU中提供了硬件支持,可以在特权级别为用户,内核或同时为两者时对事件进行计数。因此,我们希望计数从内核转换过程中完成的工作计数最多相差1或2。 >用户或用户->内核(更改CS,可能会导致GDT加载由新CS值索引的段描述符)。
cache-misses
实际上算什么事件?How does Linux perf calculate the cache-references and cache-misses events解释:
perf
显然将cache-misses
映射到计数last-level
[L1未命中的情况下,多次尝试访问L1d或L1i高速缓存中的同一行只会增加另一件事,等待相同的传入高速缓存行。因此,它不计算必须等待缓存的负载(或代码获取)。多个负载可以合并为一个访问。
[[但还请记住,代码获取需要通过iTLB,从而触发页面遍历。
缓存页面遍历负载,即通过缓存层次结构来获取它们。因此,如果确实错过了,它们就会被cache-misses
事件计数。程序的重复运行可能会导致0
缓存丢失事件。
cache-miss
事件的任何其他原因。我用perf stat -r16
运行了16次,并显示了平均值+ stddev
$ perf stat -e instructions:u,L1-dcache-loads:u,L1-dcache-load-misses:u,cache-misses:u,itlb_misses.walk_completed:u -r 16 ./exit
Performance counter stats for './exit' (16 runs):
3 instructions:u
1 L1-dcache-loads
5 L1-dcache-load-misses # 506.25% of all L1-dcache hits ( +- 6.37% )
1 cache-misses:u ( +-100.00% )
2 itlb_misses.walk_completed:u
0.0001422 +- 0.0000108 seconds time elapsed ( +- 7.57% )
请注意缓存丢失时为+ -100%。
我不知道为什么我们有2个itlb_misses.walk_completed事件,而不仅仅是1个。计数itlb_misses.miss_causes_a_walk:u
总是给我们4
。
还原为-r 1
并通过手动向上箭头反复运行,cache-misses
在3到13之间反弹。系统大部分处于空闲状态,但有一些后台网络流量。
我也不知道为什么任何东西都显示为L1D负载,或者一次负载怎么可能出现6次未命中。但是Hadi的回答是perf
的L1-dcache-load-misses事件实际上是L1D.REPLACEMENT
,因此,页面遍历可以解释这一点。 L1-dcache-loads
计为MEM_INST_RETIRED.ALL_LOADS
。 mov-immediate
不是负载,我也不会想到syscall
也是。但是也许是这样,否则硬件会错误地计数内核指令,或者某个地方存在偏离1的位置。