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

>我无法理解为什么第一个代码每次迭代有~1个循环,第二个代码每次迭代有2个循环.我用Agner的工具和perf测量.根据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.

最佳答案 根据
Agner Fog’s tables对于IvyBridge来说,movnti是2 uops,并且不能微熔丝.

所以你的第一个循环是4个融合域uops,并且每个时钟可以发出一次迭代.

nop是第5个融合域uop(即使它不占用任何执行端口,因此它是0个未融合域uop).这意味着前端只能每2个时钟发出一个循环.

另请参阅x86标记wiki,以获取有关CPU工作方式的更多链接.

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

点赞