你能调试自动矢量化循环吗?

Posted

技术标签:

【中文标题】你能调试自动矢量化循环吗?【英文标题】:can you debug auto vectorized loops? 【发布时间】:2013-06-26 21:45:39 【问题描述】:

我正在开发一个包含大量 SIMD 内在代码的代码库。现在我们有了 AVX2,我们仍然需要在不支持 AVX2 的处理器上运行 SIMD 代码,这将大大增加工作量。再加上 AVX2 shuffle 的 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 状态以进行调试。但实际上,能做到吗?

【问题讨论】:

为什么要调试它?如果您想验证它是否被矢量化,检查汇编代码或运行基准测试(在完全优化设置下)不是更好吗?如果您想查找错误,请调试非矢量化(以及其他非优化)版本。 是的,编译器错误在发生时非常糟糕。我不知道(源代码级别)调试器是否是处理它的正确工具,但无论如何,我理解。 因此,到目前为止的错误调试大多是假设性的。我绝对记得在 Visual C++ 2010 矢量化程序中提交了一个错误,该错误导致它不正确地将未对齐的负载与算术指令融合到单个 x86 指令中,导致它在未对齐的数组上运行时崩溃。 我更感兴趣的是了解向量化代码的效率,这对于汇编代码来说是非常困难的。当生成多个版本的代码时,情况会变得更糟(一些用于处理 SIMD 余数或未对齐部分,一些用于处理别名数组等)。如果我可以一次遍历 GDB 4、8 或 16 中的循环,并并排查看相应的汇编代码,那可能有助于理解。实际上,我真正想要的是一个源到源编译器,它可以用向量类型和运算符重写你的代码。有这种事吗? 使用 gcc,-fdump-tree-optimized 生成一个具有类似 C 语法的文件,显示执行了哪些优化。 【参考方案1】:

在自动矢量化代码上使用调试器很棘手,尤其是。当您想要检查需要表现不同的变量时(例如循环计数器)。

您可以使用调试版本(-O0-Og),也可以了解编译器如何向量化代码,并检查寄存器 asm 和寄存器。根据您需要追踪的错误类型,您可能会或可能不会遇到自动矢量化构建的问题。

从 cmets 看来,您更感兴趣的是检查自动矢量化的效率,而不是实际调试以修复代码中的逻辑错误。查看 asm 和基准测试可能是您最好的选择。 (甚至是一个简单的rdtsc 在调用之前/之后,或者在测试性能和正确性的单元测试中。)

有时编译器会生成多个版本的循环,例如对于输入数组重叠的情况,以及不重叠的情况。单步执行(通过指令,在 gdb 中使用 stepi,使用 layout asm)可以提供帮助,直到您找到实际完成大部分工作的循环。然后你可以专注于它是如何矢量化的。如果您想消除检查和替代版本,restrict 指针可能会有所帮助。还有p = __builtin_assume_aligned(p, 16)

您还可以使用Intel's free code analyzer 尝试静态分析一次迭代需要多少个周期。将 IACA 标记放在循环体的顶部和循环的结束括号之后,并希望 GCC 将它们放在自动矢量化循环中的适当位置,并且内联 asm 不会破坏自动矢量化。

没有任何优化答案会包含指向http://agner.org/optimize/ 的链接,所以就这样吧。

【讨论】:

以上是关于你能调试自动矢量化循环吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 GCC 强制自动矢量化

Visual Studio 2017 循环自动矢量化问题

在 GCC 的函数中禁用特定循环的自动矢量化

A[i]+=A[i+1] 是循环中的一种数据依赖性吗?可以矢量化吗?

Visual Studio 的循环矢量化(手动和自动)都有哪些资源?

如何比较矢量化和非矢量化代码