微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Haswell AVX / FMA延迟测试时间比英特尔指南慢了1个周期

如何解决Haswell AVX / FMA延迟测试时间比英特尔指南慢了1个周期

在《英特尔技术指南》中,vmulpdvfmadd213pd的延迟为5,vaddpd的延迟为3。

我写了一些测试代码,但是所有结果都慢了1个周期。

这是我的测试代码

.CODE
test_latency PROC
    vxorpd  ymm0,ymm0,ymm0
    vxorpd  ymm1,ymm1,ymm1

loop_start:
    vmulpd  ymm0,ymm1
    vmulpd  ymm0,ymm1
    sub     rcx,4
    jg      loop_start

    ret
test_latency ENDP
END
#include <stdio.h>
#include <omp.h>
#include <stdint.h>
#include <windows.h>

extern "C" void test_latency(int64_t n);

int main()
{
    SetThreadAffinityMask(GetCurrentThread(),1);   // Avoid context switch
    
    int64_t n = (int64_t)3e9;
    double start = omp_get_wtime();
    test_latency(n);
    double end = omp_get_wtime();
    double time = end - start;
    
    double freq = 3.3e9;    // My cpu frequency
    double latency = freq * time / n;
    printf("latency = %f\n",latency);
}

我的cpu是Core i5 4590,我将其频率锁定在3.3GHz。输出为:latency = 6.102484

足够奇怪,如果我将vmulpd ymm0,ymm1更改为vmulpd ymm0,ymm0,那么输出将变成:latency = 5.093745

有解释吗?我的测试代码有问题吗?

更多结果

results on Core i5 4590 @3.3GHz
vmulpd  ymm0,ymm1       6.056094
vmulpd  ymm0,ymm0       5.054515
vaddpd  ymm0,ymm1       4.038062
vaddpd  ymm0,ymm0       3.029360
vfmadd213pd ymm0,ymm1   6.052501
vfmadd213pd ymm0,ymm0   6.053163
vfmadd213pd ymm0,ymm1   6.055160
vfmadd213pd ymm0,ymm0   5.041532

(without vzeroupper)
vmulpd  xmm0,xmm0,xmm1       6.050404
vmulpd  xmm0,xmm0       5.042191
vaddpd  xmm0,xmm1       4.044518
vaddpd  xmm0,xmm0       3.024233
vfmadd213pd xmm0,xmm1   6.047219
vfmadd213pd xmm0,xmm1,xmm0   6.046022
vfmadd213pd xmm0,xmm1   6.052805
vfmadd213pd xmm0,xmm0   5.046843

(with vzeroupper)
vmulpd  xmm0,xmm1       5.062350
vmulpd  xmm0,xmm0       5.039132
vaddpd  xmm0,xmm1       3.019815
vaddpd  xmm0,xmm0       3.026791
vfmadd213pd xmm0,xmm1   5.043748
vfmadd213pd xmm0,xmm0   5.051424
vfmadd213pd xmm0,xmm1   5.049090
vfmadd213pd xmm0,xmm0   5.051947

(without vzeroupper)
mulpd   xmm0,xmm1             5.047671
mulpd   xmm0,xmm0             5.042176
addpd   xmm0,xmm1             3.019492
addpd   xmm0,xmm0             3.028642

(with vzeroupper)
mulpd   xmm0,xmm1             5.046220
mulpd   xmm0,xmm0             5.057278
addpd   xmm0,xmm1             3.025577
addpd   xmm0,xmm0             3.031238

我的GUESS

我这样更改了test_latency

.CODE
test_latency PROC
    vxorpd  ymm0,ymm1

loop_start:
    vaddpd  ymm1,ymm1  ; added this line
    vmulpd  ymm0,4
    jg      loop_start

    ret
test_latency ENDP
END

最后我得到5个周期的结果。还有其他说明可以达到相同的效果

vmovupd     ymm1,ymm0
vmovupd     ymm1,[mem]
vmovdqu     ymm1,[mem]
vxorpd      ymm1,ymm1
vpxor       ymm1,ymm1
vmulpd      ymm1,ymm1
vshufpd     ymm1,0

但是这些说明不能:

vmovupd     ymm1,ymm2  ; suppose ymm2 is zeroed
vpaddq      ymm1,ymm1
vpmulld     ymm1,ymm1
vpand       ymm1,ymm1

对于ymm指令,我想避免1个额外周期的条件是:

  1. 所有输入均来自同一域。
  2. 所有输入都足够新鲜。 (从旧的价值中移走是行不通的)

对于VEX xmm,情况似乎有些模糊。看来与上半部状态有关,但我不知道哪个更清洁:

vxorpd      ymm1,ymm1
vxorpd      xmm1,xmm1
vzeroupper

对我来说很困难。

解决方法

自从在Skylake上注意到这一点以来,我一直想写些有关此事的信息。 https://github.com/travisdowns/uarch-bench/wiki/Intel-Performance-Quirks#after-an-integer-to-fp-bypass-latency-can-be-increased-indefinitely

绕过延迟延迟是“粘滞的”:整数SIMD指令可以“感染”所有将来读取该值的指令,即使指令执行很久之后也是如此。令我惊讶的是,“感染”在整个调零习惯中幸存下来,尤其是像vxorpd这样的FP调零指令,但是我可以在SKL(i7-6700k)上重现这种效果,直接在测试循环中用{{ 1}},而不是在时间和频率上乱七八糟。)

(在Skylake上,似乎在循环发生之前连续有3条或更多perf归零指令,从而消除了额外的旁路等待时间。AFAIK,始终不执行异或归零操作 与移动消除有时会失败的方式不同,但也许区别只是在向后端发送vxorpd和第一个vpaddb之间造成了差距;在我的测试循环中,我“肮脏” /在循环之前污染寄存器。)

大概在调用方中以前使用YMM1涉及整数指令。 (TODO:调查寄存器进入此状态的情况有多普遍,以及何时可以进行异或归零!我希望只有在使用整数指令构造FP位模式(包括诸如{{1} }生成-NaN(全1)。

在Skylake上,我可以通过在异或归零之后执行vmulpd 之前循环进行修复。 (或者之前;这可能无关紧要!这可能是最佳选择,将其放在上一个dep链的末尾而不是它的开头。)


我写in a comment on another question

xsave / rstor可以解决以下问题:用 像paddd这样的SIMD整数指令会无限期地产生额外的延迟 用FP指令读取它,会影响两者的延迟 输入。例如vpcmpeqd ymm1,ymm1,ymm1然后循环vaddpd ymm1,ymm1的得分为5分 延迟,而不是通常的4,直到下一次保存/恢复。

是 绕过延迟,但即使您不触摸寄存器也仍然会发生 直到paddd明确退休(通过使用> ROB填充) uops)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。