【发布时间】:2021-08-09 06:36:39
【问题描述】:
在使用 AVX 调试某些代码时,我得到了毫无意义的结果。我将我的程序简化为以下内容:
#include <iostream>
#include <immintrin.h>
int main()
{
while (1)
{
static float v[] = {1, 2, 3, 4, 5, 6, 7, 8};
__m256 v8 = _mm256_load_ps(v);
std::cout << v8.m256_f32[2] << v8.m256_f32[5];
}
}
当我运行这个程序时,它会无休止地打印36,这是正确的(它会打印3,然后是6)。如果我在循环内的调试器中设置断点并单步执行,它会打印30。如果我删除断点并继续程序,它会再次开始打印36。我在 Release/Debug、Win32/x86(4 种组合)中看到了相同的行为。
为了能够使用 AVX,我在 Configuration Properties - C/C++ - Code Generation 中将“Enable Enhanced Instruction Set”设置为“Advanced Vector Extensions (/arch:AVX)”。我是不是忘记设置其他配置了?
由于这种行为,我无法使用调试器来调试我的真实程序(此处未包含)。这很烦人。
我做错了吗?我可以解决此问题吗?
我的 Visual Studio 是:MS Visual Studio Professional 2017,版本 15.9.3。
【问题讨论】:
-
FWIW,我没有观察到 MSVC2019、x64 调试/发布上的这种行为。我在跑步或跨步时都得到 36。
-
如果您查看 asm 代码生成,带有这些选项的 MSVC 应该会生成具有正确 vzeroupper 使用的正常 AVX 指令,例如 godbolt.org/z/YMobhKK56。 (使用
-O2,我们看到VS19.28 将向量保留在堆栈上,并对每个联合元素访问执行YMM 加载+ 洗牌,而不是利用保留调用的xmm5..15)。您是为 32 位还是 x86-64 构建?当然,这听起来像是一个调试器问题;该代码是合法的,并且应该在 MSVC 上得到很好的定义,并且在单步或不单步时改变行为是一个巨大的危险信号。 -
我在发布/调试、Win32/x86(4 种组合)中看到了相同的行为。我确实在反汇编中看到了
vzeroupper,但它位于循环内一个有趣的位置。我会再调整一下我的代码,看看我是否能理解它。 -
可能 OT:
_mm256_load_ps不需要 32 字节边界对齐吗?在您的情况下,这似乎无法保证。 -
@DanielLangr:MSVC(和 ICC)总是使用不需要对齐的 AVX 加载/存储,例如
vmovups,即使您使用需要对齐的内在函数也是如此。因此,他们通过在调试版本中故意使用需要对齐的加载/存储来消除查找未对齐数据的可能性(在这种情况下,它们不会被折叠到内存源操作数中以用于其他不需要对齐的指令。)
标签: c++ visual-studio visual-studio-debugging avx