在循环中插入 nop 以及在 movnti 存储附近读取时意外减速
Unexpected slowdown from inserting a nop in a loop, and from reading near a movnti store
- 我不明白为什么第一个代码每次迭代有 ~1 个周期,而第二个代码每次迭代有 2 个周期。我用 Agner 的工具和性能进行了测量。根据 IACA,根据我的理论计算,它应该需要 1 个周期。
每次迭代需要 1 个周期。
; array is array defined in section data
%define n 1000000
xor rcx, rcx
.begin:
movnti [array], eax
add rcx, 1
cmp rcx, n
jle .begin
每次迭代需要 2 个周期。 但是为什么呢?
; array is array defined in section data
%define n 1000000
xor rcx, rcx
.begin:
movnti [array], eax
nop
add rcx, 1
cmp rcx, n
jle .begin
这个最终版本每次迭代需要大约 27 个周期。但为什么?毕竟没有依赖链
.begin:
movnti [array], eax
mov rbx, [array+16]
add rcx, 1
cmp rcx, n
jle .begin
我的 CPU 是 IvyBridge。
根据 IvyBridge 的 Agner Fog's tables,movnti
是 2 微指令,并且不能微熔断。
所以你的第一个循环是 4 个融合域 uops,并且可以在每个时钟迭代一次。
nop
是第 5 个融合域微指令(即使它不占用任何执行端口,所以它是 0 个未融合域微指令)。这意味着前端只能每 2 个时钟发出一次循环。
另请参阅 x86 标签 wiki,了解有关 CPU 工作原理的更多链接。
第三个循环可能很慢,因为 mov rbx, [array+16]
可能是从 movnti
逐出的同一缓存行加载的。每次刷新它存储的填充缓冲区时都会发生这种情况。 (不是每个 movnti
,显然它可以重写同一个填充缓冲区中的一些字节。)
- 我不明白为什么第一个代码每次迭代有 ~1 个周期,而第二个代码每次迭代有 2 个周期。我用 Agner 的工具和性能进行了测量。根据 IACA,根据我的理论计算,它应该需要 1 个周期。
每次迭代需要 1 个周期。
; array is array defined in section data
%define n 1000000
xor rcx, rcx
.begin:
movnti [array], eax
add rcx, 1
cmp rcx, n
jle .begin
每次迭代需要 2 个周期。 但是为什么呢?
; array is array defined in section data
%define n 1000000
xor rcx, rcx
.begin:
movnti [array], eax
nop
add rcx, 1
cmp rcx, n
jle .begin
这个最终版本每次迭代需要大约 27 个周期。但为什么?毕竟没有依赖链
.begin:
movnti [array], eax
mov rbx, [array+16]
add rcx, 1
cmp rcx, n
jle .begin
我的 CPU 是 IvyBridge。
movnti
是 2 微指令,并且不能微熔断。
所以你的第一个循环是 4 个融合域 uops,并且可以在每个时钟迭代一次。
nop
是第 5 个融合域微指令(即使它不占用任何执行端口,所以它是 0 个未融合域微指令)。这意味着前端只能每 2 个时钟发出一次循环。
另请参阅 x86 标签 wiki,了解有关 CPU 工作原理的更多链接。
第三个循环可能很慢,因为 mov rbx, [array+16]
可能是从 movnti
逐出的同一缓存行加载的。每次刷新它存储的填充缓冲区时都会发生这种情况。 (不是每个 movnti
,显然它可以重写同一个填充缓冲区中的一些字节。)