【问题标题】:What are these extra disassembly instructions when using SIMD intrinsics?使用 SIMD 内在函数时,这些额外的反汇编指令是什么?
【发布时间】:2016-04-04 11:20:00
【问题描述】:

我正在测试通过 RyuJIT 使用 SIMD 指令可以获得什么样的加速,我看到了一些我没想到的反汇编指令。我将代码基于来自 RyuJIT 团队的 Kevin Frei 的this blog post,以及相关的帖子here。函数如下:

static void AddPointwiseSimd(float[] a, float[] b) {
    int simdLength = Vector<float>.Count;
    int i = 0;
    for (i = 0; i < a.Length - simdLength; i += simdLength) {
        Vector<float> va = new Vector<float>(a, i);
        Vector<float> vb = new Vector<float>(b, i);
        va += vb;
        va.CopyTo(a, i);
    }
}

我正在查询的反汇编部分将数组值复制到Vector&lt;float&gt;。大部分反汇编与 Kevin 和 Sasha 的帖子中的类似,但我强调了一些额外的说明(以及我混淆的注释),这些说明没有出现在他们的反汇编中:

;// Vector<float> va = new Vector<float>(a, i);
  cmp eax,r8d              ; <-- Unexpected - Compare a.Length to i?
  jae 00007FFB17DB6D5F     ; <-- Unexpected - Jump to range check failure
  lea r10d,[rax+3] 
  cmp r10d,r8d 
  jae 00007FFB17DB6D5F 
  mov r11,rcx              ; <-- Unexpected - Extra register copy?
  movups xmm0,xmmword ptr [r11+rax*4+10h  ]

;// Vector<float> vb = new Vector<float>(b, i);
  cmp eax,r9d              ; <-- Unexpected - Compare b.Length to i?
  jae 00007FFB17DB6D5F     ; <-- Unexpected - Jump to range check failure
  cmp r10d,r9d 
  jae 00007FFB17DB6D5F 
  movups xmm1,xmmword ptr [rdx+rax*4+10h]

注意循环范围检查符合预期:

;// for (i = 0; i < a.Length - simdLength; i += simdLength) {
  add eax,4  
  cmp r9d,eax  
  jg loop

所以我不知道为什么要与eax 进行额外比较。谁能解释为什么我会看到这些额外的说明以及是否可以摆脱它们。

如果它与项目设置有关,我有一个非常相似的项目显示相同的问题here on github(请参阅FloatSimdProcessor.HwAcceleratedSumInPlace()UShortSimdProcessor.HwAcceleratedSumInPlaceUnchecked())。

【问题讨论】:

    标签: c# .net simd ryujit


    【解决方案1】:

    我将注释我看到的代码生成,对于像 Haswell 这样支持 AVX2 的处理器,它一次可以移动 8 个浮点数:

    00007FFA1ECD4E20  push        rsi
    00007FFA1ECD4E21  sub         rsp,20h  
    
    00007FFA1ECD4E25  xor         eax,eax                       ; i = 0
    00007FFA1ECD4E27  mov         r8d,dword ptr [rcx+8]         ; a.Length
    00007FFA1ECD4E2B  lea         r9d,[r8-8]                    ; a.Length - simdLength
    00007FFA1ECD4E2F  test        r9d,r9d                       ; if (i >= a.Length - simdLength)
    00007FFA1ECD4E32  jle         00007FFA1ECD4E75              ; then skip loop 
    
    00007FFA1ECD4E34  mov         r10d,dword ptr [rdx+8]        ; b.Length
    00007FFA1ECD4E38  cmp         eax,r8d                       ; if (i >= a.Length)
    00007FFA1ECD4E3B  jae         00007FFA1ECD4E7B              ; then OutOfRangeException
    00007FFA1ECD4E3D  lea         r11d,[rax+7]                  ; i+7
    00007FFA1ECD4E41  cmp         r11d,r8d                      ; if (i+7 >= a.Length)
    00007FFA1ECD4E44  jae         00007FFA1ECD4E7B              ; then OutOfRangeException
    
    00007FFA1ECD4E46  mov         rsi,rcx                       ; move a[i..i+7]
    00007FFA1ECD4E49  vmovupd     ymm0,ymmword ptr [rsi+rax*4+10h]  
    
    00007FFA1ECD4E50  cmp         eax,r10d                      ; same as above 
    00007FFA1ECD4E53  jae         00007FFA1ECD4E7B              ; but for b
    00007FFA1ECD4E55  cmp         r11d,r10d  
    00007FFA1ECD4E58  jae         00007FFA1ECD4E7B  
    00007FFA1ECD4E5A  vmovupd     ymm1,ymmword ptr [rdx+rax*4+10h]  
    
    00007FFA1ECD4E61  vaddps      ymm0,ymm0,ymm1                ; a[i..] + b[i...]
    00007FFA1ECD4E66  vmovupd     ymmword ptr [rsi+rax*4+10h],ymm0  
    
    00007FFA1ECD4E6D  add         eax,8                         ; i += 8
    00007FFA1ECD4E70  cmp         r9d,eax                       ; if (i < a.Length)
    00007FFA1ECD4E73  jg          00007FFA1ECD4E38              ; then loop
    
    00007FFA1ECD4E75  add         rsp,20h  
    00007FFA1ECD4E79  pop         rsi  
    00007FFA1ECD4E7A  ret  
    

    所以 eax 比较的是博客文章中提到的那些“讨厌的绑定检查”。博客文章提供了一个尚未实际实现的优化版本,实际代码现在检查同时移动的 8 个浮点数的第一个和最后一个索引。博客文章中的评论“希望我们能够充分加强边界检查消除工作”是一项未完成的任务:)

    mov rsi,rcx 指令也出现在博客文章中,似乎是寄存器分配器的限制。可能受RCX作为重要寄存器的影响,它通常存储this。我认为,做这项工作以优化这一点并不重要,寄存器到寄存器的移动需要 0 个周期,因为它们只影响寄存器重命名。

    请注意 SSE2 和 AVX2 之间的区别是多么难看,虽然代码一次移动并添加了 8 个浮点数,但实际上只使用了其中的 4 个。 Vector&lt;float&gt;.Count 是 4,不管处理器的风格如何,在桌面上留下 2x 性能。我猜很难隐藏实现细节。

    【讨论】:

      猜你喜欢
      • 2017-08-25
      • 1970-01-01
      • 2015-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      • 2018-10-16
      • 1970-01-01
      相关资源
      最近更新 更多