【发布时间】:2021-04-30 12:43:19
【问题描述】:
我很难解释英特尔性能事件报告。
考虑以下主要读取/写入内存的简单程序:
#include <stdint.h>
#include <stdio.h>
volatile uint32_t a;
volatile uint32_t b;
int main() {
printf("&a=%p\n&b=%p\n", &a, &b);
for(size_t i = 0; i < 1000000000LL; i++) {
a ^= (uint32_t) i;
b += (uint32_t) i;
b ^= a;
}
return 0;
}
我用gcc -O2编译它并在perf下运行:
# gcc -g -O2 a.c
# perf stat -a ./a.out
&a=0x55a4bcf5f038
&b=0x55a4bcf5f034
Performance counter stats for 'system wide':
32,646.97 msec cpu-clock # 15.974 CPUs utilized
374 context-switches # 0.011 K/sec
1 cpu-migrations # 0.000 K/sec
1 page-faults # 0.000 K/sec
10,176,974,023 cycles # 0.312 GHz
13,010,322,410 instructions # 1.28 insn per cycle
1,002,214,919 branches # 30.699 M/sec
123,960 branch-misses # 0.01% of all branches
2.043727462 seconds time elapsed
# perf record -a ./a.out
&a=0x5589cc1fd038
&b=0x5589cc1fd034
[ perf record: Woken up 3 times to write data ]
[ perf record: Captured and wrote 0.997 MB perf.data (9269 samples) ]
# perf annotate
perf annotate 的结果(我为内存加载/存储注释):
Percent│ for(size_t i = 0; i < 1000000000LL; i ++) {
│ xor %eax,%eax
│ nop
│ a ^= (uint32_t) i;
│28: mov a,%edx // 32-bit load
│ xor %eax,%edx
9.74 │ mov %edx,a // 32-bit store
│ b += (uint32_t) i;
12.12 │ mov b,%edx // 32-bit load
8.79 │ add %eax,%edx
│ for(size_t i = 0; i < 1000000000LL; i ++) {
│ add $0x1,%rax
│ b += (uint32_t) i;
18.69 │ mov %edx,b // 32-bit store
│ b ^= a;
0.04 │ mov a,%ecx // 32-bit load
22.39 │ mov b,%edx // 32-bit load
8.92 │ xor %ecx,%edx
19.31 │ mov %edx,b // 32-bit store
│ for(size_t i = 0; i < 1000000000LL; i ++) {
│ cmp $0x3b9aca00,%rax
│ ↑ jne 28
│ }
│ return 0;
│ }
│ xor %eax,%eax
│ add $0x8,%rsp
│ ← retq
我的观察:
- 从 1.28
insn per cycle我得出结论,程序主要是内存绑定的。 -
a和b似乎位于同一缓存行中,彼此相邻。
我的问题:
- 对于各种内存加载和存储,CPU 时间不应该更加一致吗?
- 为什么第一次内存加载 (
mov a,%edx) 的 CPU 时间为零? - 为什么第三次加载的时间是
mov a,%ecx0.04%,而旁边的时间是mov b,%edx22.39%? - 为什么有些指令需要 0 时间?循环由 14 条指令组成,因此每条指令都必须贡献一些可观察的时间。
注意事项:
操作系统:Linux 4.19.0-amd64,CPU:Intel Core i9-9900K,100% 空闲系统(也在 i7-7700 上测试,结果相同)。
【问题讨论】:
-
奇怪的代码不高亮
标签: performance assembly x86-64 perf micro-optimization