1

我正在开发一个包含大量 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 的值表示 <optimized out>,而有时它会跳出 20。

似乎主要问题是很难将 SIMD 状态映射到原始 C 状态以进行调试。但实际上,能做到吗?

4

1 回答 1

0

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

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

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

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

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

任何优化答案都不会包含指向http://agner.org/optimize/的链接,所以你去吧。

于 2015-08-02T00:12:11.643 回答