首先,我在IvyBridge上有以下设置,在注释位置插入测量负载代码。 buf
的前8个字节存储了buf
本身的地址,我使用这个来创建循环依赖:
section .bss
align 64
buf: resb 64
section .text
global _start
_start:
mov rcx, 1000000000
mov qword [buf], buf
mov rax, buf
loop:
; I will insert payload here
; as is described below
dec rcx
jne loop
xor rdi, rdi
mov rax, 60
syscall
案例1:
我将数据插入到负载位置:
mov qword [rax+8], 8
mov rax, [rax]
perf
显示循环每次需要 5.4c。这个数据比较容易理解,因为 L1d 的延迟为 4 个时钟周期。
案例二:
我将这两条指令的顺序颠倒了:
mov rax, [rax]
mov qword [rax+8], 8
结果突然变成了9c/iter。我不明白为什么。因为下一次迭代的第一个指令不依赖于当前迭代的第二个指令,所以这种设置在案例1中应该是相同的。
我还使用了IACA工具对这两种情况进行了静态分析,但该工具不可靠,因为它预测这两种情况的结果都是5.71c/iter,这与实验结果相矛盾。
案例3:
然后我在案例2中插入了一个无关的mov指令:
mov rax, [rax]
mov qword [rax+8], 8
mov rbx, [rax+16]
现在结果变为6.8c/iter。但是一个无关的
mov
怎么会将速度从9c/iter提高到6.8c/iter呢?与之前情况一样,IACA工具预测的结果是错误的,显示为5.24c/iter。
我现在完全困惑了,如何理解上述结果?
附加信息编辑:
在情况1和2中,有一个地址
rax+8
。如果将rax+8
更改为rax+16
或rax+24
,情况1和2将保持相同的结果。但当它被更改为rax+32
时,令人惊讶的事情发生了:情况1变成了5.3c/iter,而情况2突然变成了4.2c/iter。更多
perf
事件信息编辑:$ perf stat -ecycles,ld_blocks_partial.address_alias,int_misc.recovery_cycles,machine_clears.count,uops_executed.stall_cycles,resource_stalls.any ./a.out
[rax+8]
的第一个例子:
5,429,070,287 cycles (66.53%)
6,941 ld_blocks_partial.address_alias (66.75%)
426,528 int_misc.recovery_cycles (66.83%)
17,117 machine_clears.count (66.84%)
2,182,476,446 uops_executed.stall_cycles (66.63%)
4,386,210,668 resource_stalls.any (66.41%)
对于[rax+8]
的第二个情况:
9,018,343,290 cycles (66.59%)
8,266 ld_blocks_partial.address_alias (66.73%)
377,824 int_misc.recovery_cycles (66.76%)
10,159 machine_clears.count (66.76%)
7,010,861,225 uops_executed.stall_cycles (66.65%)
7,993,995,420 resource_stalls.any (66.51%)
对于[rax+8]
,第三种情况:
6,810,946,768 cycles (66.69%)
1,641 ld_blocks_partial.address_alias (66.73%)
223,062 int_misc.recovery_cycles (66.73%)
7,349 machine_clears.count (66.74%)
3,618,236,557 uops_executed.stall_cycles (66.58%)
5,777,653,144 resource_stalls.any (66.53%)
[rax+32]
的第二个情况:
4,202,233,246 cycles (66.68%)
2,969 ld_blocks_partial.address_alias (66.68%)
149,308 int_misc.recovery_cycles (66.68%)
4,522 machine_clears.count (66.68%)
1,202,497,606 uops_executed.stall_cycles (66.64%)
3,179,044,737 resource_stalls.any (66.64%)
[rel buf+8]
,所有情况都变成了4c/iter,所以这与相关存储有关。出于好奇,这种微架构异常在实践中发生的频率有多高?这段代码看起来很简单,对于更复杂的实际代码,我想知道是否真的可能预测关键部分的周期数。 - user10865622STALLS_LDM_PENDING
等于CYCLES_NO_EXECUTE
= 6c,表明负载延迟对性能的有效影响为8.4c中的6c,且负载延迟至少为6c。UOPS_EXECUTED_PORT.PORT_X
事件显示PORT_2
+PORT_3
+PORT_7
= 5B uops,但预期值为2B。然而,PORT_4
正如预期的是1B。由于某种原因,load uop正在被重放。 - Hadi BraisSTALLS_LDM_PENDING
可能捕捉到了这种效应。OP的结果表明,这个问题可能存在于IvB上,但具有不同的性能影响... - Hadi Brais