c++ - 使用 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 代码,所以如果我做错了什么,我将不胜感激。
解决方案
推荐阅读
- android - 在 Ardroid 中使用本地 JSON 文件与使用 JSON 字符串
- javascript - 幻灯片效果,每张幻灯片 3 张图像
- c# - 为什么一部安卓手机会自动保存图片而另一部没有?
- python - Python.h 的递归包含导致编译错误
- java - 没有命名空间的肥皂响应
- python - 如何让机器人只对同一条消息的 add_reaction 做出响应?
- google-cloud-platform - Google OAuth 验证在应用域之外设置授权域
- javascript - 为什么隐藏后此代码不显示按钮?
- javascript - 假路线(经纬度)库
- javascript - 如何获取跟踪的 JS 列表字段的值?