您创建了main 而不是_start,并且可能将其构建到动态链接的可执行文件中!所以有所有的 CRT 启动代码、初始化 libc 和几个系统调用。运行strace ./test 并查看它发出了多少系统调用。 (当然,用户空间中有很多工作不涉及系统调用)。
更有趣的是静态链接的可执行文件,它只使用syscall 指令从_start 入口点进行_exit(0) 或exit_group(0) 系统调用。
给定一个包含这些内容的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
我告诉它计算 cache-misses:u 以仅测量 用户空间 缓存未命中,而不是进程正在运行的核心上的所有内容。 (这将包括在进入用户空间之前和处理 exit_group() 系统调用时内核缓存未命中。以及潜在的中断处理程序。
(当特权级别是用户、内核或两者时,PMU 中有硬件支持对事件进行计数。所以我们应该期望计数在从内核转换期间完成的计数中最多减少 1 或 2 ->user 或 user->kernel。(更改 CS,可能导致从 GDT 加载由新 CS 值索引的段描述符)。
但是cache-misses 到底算什么事件呢?
How does Linux perf calculate the cache-references and cache-misses events 解释:
perf 显然将 cache-misses 映射到计算 last-level 缓存未命中的硬件事件。所以它类似于 DRAM 访问次数。
多次尝试访问 L1d 或 L1i 缓存中的同一行,而 L1 未命中已经未完成,只会添加另一个等待同一传入缓存行的内容。所以它不计算必须等待缓存的加载(或代码获取)。
多个负载可以合并为一个访问。
但也请记住,代码获取需要通过 iTLB,触发页面遍历。 页面遍历加载被缓存,即它们是通过缓存层次结构获取的。因此,如果他们确实错过了,他们会被 cache-misses 事件计算在内。
程序的重复运行可能导致0 cache-miss 事件。 可执行二进制文件是一个文件,该文件由页面缓存缓存(操作系统的磁盘缓存)。该物理内存被映射到运行它的进程的地址空间。它当然可以在进程启动/停止期间在 L3 中保持热状态。更有趣的是,显然页表也很热。 (不是字面上的“保持”热度;我假设内核每次都必须编写一个新的。但大概页面遍历器至少在 L3 缓存中命中。)
或者至少其他任何导致“额外”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 也是。但也许是这样,否则硬件会错误地计算内核指令或者某处有一个 off-by-1。