我正在开发一个包含许多SIMD内部代码的代码库。现在我们有了AVX2,我们仍然需要让SIMD代码在不支持AVX2的处理器上运行,这将大大增加工作量。再加上AVX2改组的128位车道穿越限制也使事情变得复杂。由于这些原因,现在是更多地依赖自动矢量化的好时机。吓到我的主要事情是可能发生单个无辜更改而杀死并行性,以及在出现问题时调试自动矢量化代码的可能性。
我已经使用g ++ -O1 -g -ftree-vectorize编译了以下代码,并尝试逐步使用GDB(有人知道为什么-ftree-vectorize无法与-O0一起工作吗?)
float a[1000], b[1000], c[1000];
int main(int argc, char **argv)
{
for (int i = 0; i < argc; ++i)
c[i] = a[i] + b[i];
return 0;
}
但没有任何有意义的结果。例如,有时i的值表示,而其他时候则为20。
似乎主要的问题是很难将SIMD状态映射到原始C状态以进行调试。但实际上,可以做到吗?
最佳答案
特别是在自动矢量化代码上使用调试器非常棘手。当您想要检查需要表现不同的变量时(例如循环计数器)。
您可以使用调试版本(-O0
或-Og
),也可以了解编译器如何对代码进行矢量化处理,并检查寄存器asm和寄存器。根据需要跟踪的错误类型,自动矢量化构建可能会出现问题,也可能不会出现问题。
从注释中听起来,您似乎更感兴趣于检查自动矢量化的效率,而不是实际调试以修复代码中的逻辑错误。查看asm和基准可能是您最好的选择。 (甚至在通话之前或之后,或者在测试性能和正确性的单元测试中,甚至是一个简单的rdtsc
。)
有时编译器会生成循环的多个版本,例如对于输入数组重叠的情况,对于不重叠的情况。单步执行(通过指令,在gdb中使用stepi
,在layout asm
中使用)可以帮助您,直到您发现实际上完成大部分工作的循环为止。然后,您可以专注于向量化的方式。如果要取消检查和替代版本,restrict
指针可能会有所帮助。还有p = __builtin_assume_aligned(p, 16)
。
您也可以使用Intel's free code analyzer尝试静态分析一次迭代需要多少个周期。将IACA标记放在循环主体的顶部和循环的结束括号之后,希望GCC将其放在自动向量化循环的适当位置,并且内联汇编不会破坏自动向量化。
指向http://agner.org/optimize/的链接无法提供优化答案,因此您可以开始。
关于c++ - 您可以调试自动矢量化循环吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17331029/