首页 > 解决方案 > 使用 AVX2 显着降低功能

问题描述

我认识到 Clang (10.0) 和 MSVC (16.7)从具有一些 AVX2 内在函数的同一段 C++ 代码生成具有显着不同性能的程序集(~3.3ns对于 Clang,~对于 MSVC)。8ns


该代码正在计算10可以从给定整数 ( rcx) 中分解出的最大指数,并在分解出 的最大幂后计算商10

为了阐明代码的作用,它计算(以 10 为基数)给定整数的尾随零,并通过删除这些尾随零来计算您获得的数字。例如,当输入为 时63700,输出为{637, 2},当输入为 时1230000,输出为{123, 4}

这是一个可以重现代码的godbolt链接:https ://godbolt.org/z/5xoK3G

在第 231 行,您可以选择使用 lambda 的一个(生成vmovups重新加载 2 个标量存储并复制它)和不使用 lambda 的一个(不生成那些vmovups)。


为了弄清楚原因,我做了几个测试。事实证明,不同之处在于 MSVC 的版本使用两个vmovups(一个用于加载,一个用于存储)来写回结果,而 Clang 使用两个mov(都用于存储)代替。

为了证实这个假设,我做了几件事来强制 MSVC 生成mov而不是vmovups,生成的代码执行几乎与 Clang 生成的代码相同。

问题是,为什么vmovups在这种情况下使用(或其他类似的向量移动指令)会大大降低性能?

我附加了两个由 MSVC 生成的程序集输出,一个带有vmovups(因此执行 ~ 8ns)和一个不带vmovups(因此执行 ~ 3.3ns):

With vmovups: (这个版本还增加/减少堆栈指针;这是必要的吗?)

00007FF7227114B0  sub         rsp,18h  
00007FF7227114B4  mov         r8,rcx  
00007FF7227114B7  lea         rcx,[divtest_table_holder<unsigned int,5,9>::table (07FF7227133B0h)]  
00007FF7227114BE  vmovd       xmm0,r8d  
00007FF7227114C3  vpbroadcastd ymm0,xmm0  
00007FF7227114C8  vpmulld     ymm1,ymm0,ymmword ptr [divtest_table_holder<unsigned int,5,9>::table+4h (07FF7227133B4h)]  
00007FF7227114D1  vpminud     ymm0,ymm1,ymmword ptr [divtest_table_holder<unsigned int,5,9>::table+28h (07FF7227133D8h)]  
00007FF7227114DA  vpcmpeqd    ymm1,ymm0,ymm1  
00007FF7227114DE  vpmovmskb   eax,ymm1  
00007FF7227114E2  popcnt      edx,eax  
00007FF7227114E6  tzcnt       eax,r8d  
00007FF7227114EB  shr         edx,2  
00007FF7227114EE  cmp         eax,edx  
00007FF7227114F0  cmovl       edx,eax  
00007FF7227114F3  movsxd      rax,edx  
00007FF7227114F6  mov         dword ptr [rsp+8],edx  
00007FF7227114FA  mov         ecx,dword ptr [rcx+rax*4]  
00007FF7227114FD  imul        rcx,r8  
00007FF722711501  mov         eax,edx  
00007FF722711503  shrx        rcx,rcx,rax  
00007FF722711508  mov         qword ptr [rsp],rcx  
00007FF72271150C  vmovups     xmm0,xmmword ptr [rsp]  
00007FF722711511  vmovups     xmmword ptr [rsp],xmm0  
00007FF722711516  vzeroupper  
00007FF722711519  add         rsp,18h  
00007FF72271151D  ret  

没有vmovups

00007FF7475414B0  mov         r8,rcx  
00007FF7475414B3  lea         rcx,[divtest_table_holder<unsigned int,5,9>::table (07FF7475433B0h)]  
00007FF7475414BA  vmovd       xmm0,r8d  
00007FF7475414BF  vpbroadcastd ymm0,xmm0  
00007FF7475414C4  vpmulld     ymm2,ymm0,ymmword ptr [divtest_table_holder<unsigned int,5,9>::table+4h (07FF7475433B4h)]  
00007FF7475414CD  vpminud     ymm1,ymm2,ymmword ptr [divtest_table_holder<unsigned int,5,9>::table+28h (07FF7475433D8h)]  
00007FF7475414D6  vpcmpeqd    ymm2,ymm1,ymm2  
00007FF7475414DA  vpmovmskb   eax,ymm2  
00007FF7475414DE  popcnt      edx,eax  
00007FF7475414E2  tzcnt       eax,r8d  
00007FF7475414E7  shr         edx,2  
00007FF7475414EA  cmp         eax,edx  
00007FF7475414EC  cmovl       edx,eax  
00007FF7475414EF  movsxd      rax,edx  
00007FF7475414F2  mov         ecx,dword ptr [rcx+rax*4]  
00007FF7475414F5  imul        rcx,r8  
00007FF7475414F9  mov         eax,edx  
00007FF7475414FB  shrx        rcx,rcx,rax  
00007FF747541500  mov         qword ptr [rsp+8],rcx  
00007FF747541505  mov         dword ptr [rsp+8],edx  
00007FF747541509  vzeroupper  
00007FF74754150C  ret  

另外,由于这是我编写的第一个 SIMD 代码,所以如果我做错了什么,我将不胜感激。

标签: c++performancevisual-c++x86-64avx2

解决方案


推荐阅读