用于复数乘法的汇编代码/AVX 指令。 (GCC 内联汇编)

Posted

技术标签:

【中文标题】用于复数乘法的汇编代码/AVX 指令。 (GCC 内联汇编)【英文标题】:Assembly code/AVX instructions for multiplication of complex numbers. (GCC inline assembly) 【发布时间】:2013-04-02 14:35:36 【问题描述】:

我们正在运行一项科学计划,并且希望实现 AVX 功能。整个程序(用 Fortran+C 编写)将被向量化,目前我正在尝试在 GCC 内联汇编中实现复数乘法。

汇编代码接受 4 个复数并一次执行两个复数乘法:

v2complex cmult(v2complex *a, v2complex *b) 
    v2complex ret;
    asm (
        "vmovupd %2,%%ymm1;"
        "vmovupd %2, %%ymm2;"
        "vmovddup %%ymm2, %%ymm2;"
        "vshufpd $15,%%ymm1,%%ymm1,%%ymm1;"
        "vmulpd %1, %%ymm2, %%ymm2;"
        "vmulpd %1, %%ymm1, %%ymm1;"
        "vshufpd $5,%%ymm1,%%ymm1, %%ymm1;"
        "vaddsubpd %%ymm1, %%ymm2,%%ymm1;"
        "vmovupd %%ymm1, %0;"
        :
        "=m"(ret)
        :
        "m" (*a),
        "m" (*b)
        );
    return ret;

其中 a 和 b 是 256 位双精度:

typedef union v2complex 
    __m256d v;
    complex c[2];
 v2complex;

问题是代码大多会产生正确的结果,但有时会失败。

我对组装很陌生,但我试图自己弄清楚。似乎 C 程序(优化的 -O3)与汇编代码中使用的寄存器 ymm 交互。例如,我可以在执行乘法之前打印其中一个值(例如 a),并且程序永远不会给出错误的结果。

我的问题是如何告诉 GCC 不要与 ymm 交互。我没能做到 将ymm 放到被破坏的寄存器列表中。

【问题讨论】:

【参考方案1】:

正如您推测的那样,问题在于您没有告诉 GCC 您正在破坏哪些寄存器。如果他们还不支持将 YMM 寄存器放入 clobber 列表中,我会感到惊讶;你使用的是什么版本的 GCC?

无论如何,将相应的 XMM 寄存器放在 clobber 列表中几乎肯定就足够了:

: "=m" (ret) : "m" (*a), "m" (*b) : "%xmm1", "%xmm2");

其他一些注意事项:

您将两个输入加载两次,效率低下。没有理由这样做。 我会使用"r" (a), "r" (b) 作为约束,并像vmovupd (%2), %%ymm1 这样写我的负载。生成的代码可能没有区别,但它似乎更符合习惯用法。 在执行任何 SSE 代码之前,不要忘记在 AVX 代码后面加上 vzeroupper,以避免(大)停顿。

【讨论】:

非常感谢,解决了问题=)。我正在使用 gcc 4.7.2 和 thx 为您提供建议。 不要将“r” (a), “r” (b)vmovupd (%2), %%ymm1 等一起使用,GCC 将假定 *a 和 *b 未被访问(除非您添加“内存”破坏者)。【参考方案2】:

我加了两个cmets,不直接回答你的问题:

我强烈建议使用编译器内在函数而不是直接汇编。这样,编译器会负责寄存器分配,并且可以更好地优化代码(内联方法、重新排序指令等) Agner Fog 有一个 C++ vector class library 优化矢量化操作,包括对复数的操作。即使您可能无法在 C 代码中直接使用他的库,他的优化代码也可能是一个很好的起点;请参阅the zipped source code 中的src/special/complexvec.h

【讨论】:

谢谢,我会看看它,即使我将无法使用它。实际上,我已经编译了两个版本,内部代码和程序集,我想知道为什么程序集运行不好,因为它们都编译为相同的优化程序集代码(objdump -S ..)。 但是内在函数也有缺点:编译器负责寄存器分配和重新排序指令等。通常编译器不会更好地做到这一点。

以上是关于用于复数乘法的汇编代码/AVX 指令。 (GCC 内联汇编)的主要内容,如果未能解决你的问题,请参考以下文章

使用 openmp 并行化矩阵乘法并使用 avx2 进行矢量化

在 GCC 上设置打包的 long long 的正确对齐以与 avx2 指令一起使用

AVX2 的汇编错误

如何在 AVX 中使用融合乘法和加法来处理 16 位压缩整数

非法指令 gcc 汇编程序

GCC + LD + NDISASM = 大量的汇编指令