在循环中插入 nop 以及在 movnti 存储附近读取时意外减速

Unexpected slowdown from inserting a nop in a loop, and from reading near a movnti store

  1. 我不明白为什么第一个代码每次迭代有 ~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 个时钟发出一次循环。

另请参阅 标签 wiki,了解有关 CPU 工作原理的更多链接。


第三个循环可能很慢,因为 mov rbx, [array+16] 可能是从 movnti 逐出的同一缓存行加载的。每次刷新它存储的填充缓冲区时都会发生这种情况。 (不是每个 movnti,显然它可以重写同一个填充缓冲区中的一些字节。)