是否有可能影响GCC / Clang / Intel ICPC XMM / YMM寄存器分配?

时间:2016-11-12 19:55:43

标签: gcc assembly clang intrinsics icc

我有一个高度优化的函数,在内循环中重复调用,用SSE2 / AVX2加速写入。经过一些改进,现在我正在接近理论上的最佳性能(基于指令延迟和吞吐量)。但是,性能不太方便。问题在于有超过16个__m128i / __256i个变量。当然,只有16个可以在寄存器中分配,剩下的就在堆栈中。该功能或多或少如下,

void eval(size_t n, __m128i *rk /* other inputs */)
{
    __m128i xmmk0 = rk[0];
    // ...
    __m128i xmmk6 = rk[6];
    __m128i xmmk;

    __m128i xmmk[Rounds - 6];
    // copy rk[7] to r[Rounds] to xmmk

    while (n >= 8) {
        n -= 8;

        __m128i xmm0 = /* initialize state xmm0 */
        // do the same for xmm1 - xmm7

        // round 0
        xmm0 = /* a few instructions involving xmm0 and xmmk0 */;
        // do the same for xmm1 - xmm7

        // do the same for round 1 to 6, using xmmk1, ..., xmmk6

        // round 7, copy xmmk[0] to a temporary __m128i variable
        xmm0 = /* a few instructions involving xmm0 and xmmk[0] */;
        // do the same for xmm1 - xmm7

        // do the same for round 7 to Rounds, using xmmk[1], xmmk[Rounds - 7]
    }
}

涉及的变量超过16个__m128i。我能得到的最佳性能是编译器在寄存器中将xmm0分配给xmm7,状态xmmk0xmmk6,前7个循环常量,并使用保留寄存器作为临时加载剩余轮次的常量。当以类似于上面的方式编写时,GCC / clang正在这样做,但是英特尔ICPC在堆栈上分配了一些xmm0xmm7变量并引入了一些不必要的内存移动。相反,我的写作方式类似于以下

__m128i xmmk[Rounds + 1]; // copy from input rk
// let compiler to figure out which of them are allocated on stack and which in registers,

然后GCC / ICPC在寄存器分配方面做得很好,而在前一种情况下,clang陷入类似于ICPC的情况。

当然,声明类型为__m128i的变量不会使其成为寄存器,也不会在堆栈数组中声明它会阻止编译器为其分配寄存器。

我能够编写一个ASM实现,​​它完全符合我的要求。但是,实际函数涉及一些指定为模板策略的编译时常量。因此,更需要在内部函数的C ++中实现它们。

我想知道的是,是否有办法影响这些编译器如何执行寄存器分配。由于快速L1缓存,通常性能差异仅仅是几个周期。然而,当高度优化时,当内环仅需要一百个周期时,由于不必要的存储器移动导致的十几个周期差异转化为20%的整体性能差异。据我所知,没有办法让编译器完全像手工编写的程序集一样。但如果我至少能给他们一些提示,那将会很有帮助。例如,我知道加载圆形常量的延迟可以被其他指令隐藏。因此,更希望将它们分配到堆栈而不是状态变量xmm0xmm7

0 个答案:

没有答案