9

我正在尝试使用 SSE 或新的 AVX 指令为 Windows x64 目标编写一些计算密集型代码,在 GCC 4.5.2 和 4.6.1、MinGW64(TDM GCC 构建和一些自定义构建)中编译。我的编译器选项是-O3 -mavx. (-m64暗示)

简而言之,我想对 4 个打包浮点数的 3D 向量执行一些冗长的计算。这需要 4x3=12 xmm 或 ymm 寄存器用于存储,以及 2 或 3 个寄存器用于临时结果。恕我直言,这应该恰好适合 64 位目标可用的 16 个可用 SSE(或 AVX)寄存器。然而,GCC 产生了一个非常次优的带有寄存器溢出的代码,只使用寄存器xmm0-xmm10并将数据从堆栈中移入和移入堆栈。我的问题是:

有没有办法说服 GCC 使用所有的寄存器xmm0-xmm15

要修正想法,请考虑以下 SSE 代码(仅用于说明):

void example(vect<__m128> q1, vect<__m128> q2, vect<__m128>& a1, vect<__m128>& a2) {
    for (int i=0; i < 10; i++) {
        vect<__m128> v = q2 - q1;
        a1 += v;
//      a2 -= v;

        q2 *= _mm_set1_ps(2.);
    }
}

这里vect<__m128>只是简单的 a struct of 3 __m128,通过标量进行自然加法和乘法运算。当该行a2 -= v被注释掉时,即我们只需要 3x3 寄存器来存储,因为我们忽略a2了 ,生成的代码确实很简单,没有移动,一切都在寄存器中执行xmm0-xmm10。当我删除评论a2 -= v时,代码非常糟糕,寄存器和堆栈之间有很多改组。即使编译器可以只使用寄存器xmm11-xmm13或其他东西。

实际上,我还没有看到 GCCxmm11-xmm15在我的所有代码中的任何地方使用任何寄存器。我究竟做错了什么?我知道它们是被调用者保存的寄存器,但是通过简化循环代码,这种开销是完全合理的。

4

2 回答 2

14

两点:

  • 首先,你做了很多假设。寄存器溢出在 x86 CPU 上非常便宜(由于快速的 L1 缓存和寄存器阴影和其他技巧),并且仅 64 位的寄存器访问成本更高(就更大的指令而言),所以它可能只是 GCC 的版本与您想要的一样快或更快。
  • 其次,GCC 与任何编译器一样,会尽其所能进行最佳的寄存器分配。没有“请做更好的寄存器分配”选项,因为如果有,它总是被启用的。编译器并不想惹你生气。(我记得,寄存器分配是一个 NP 完全问题,所以编译器永远无法生成完美的解决方案。它所能做的最好的就是近似)

所以,如果你想要更好的寄存器分配,你基本上有两个选择:

  • 编写一个更好的寄存器分配器,并将其修补到 GCC 中,或者
  • 绕过 GCC 并在汇编中重写函数,因此您可以准确控制何时使用哪些寄存器。
于 2011-05-11T07:40:08.603 回答
6

实际上,您看到的不是溢出,而是 gcc 在内存中对 a1 和 a2 进行操作,因为它不知道它们是否有别名。如果您将最后两个参数声明为vect<__m128>& __restrict__GCC 可以并且将注册分配 a1 和 a2。

于 2014-07-18T00:49:29.703 回答