【问题标题】:Getting GCC to generate a PTEST instruction when using vector extensions使用向量扩展时让 GCC 生成 PTEST 指令
【发布时间】:2015-06-10 22:11:20
【问题描述】:

当使用 C 的 GCC 向量扩展时,如何检查向量上的所有值是否为零?

例如:

#include <stdint.h>

typedef uint32_t v8ui __attribute__ ((vector_size (32)));

v8ui*
foo(v8ui *mem) {
    v8ui v;
    for ( v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
          v[0] || v[1] || v[2] || v[3] || v[4] || v[5] || v[6] || v[7];
          mem++)
        v &= *(mem);

    return mem;
}

SSE4.2 具有 PTEST 指令,它允许运行类似于 for 条件的测试,但 GCC 生成的代码只是解包向量并一一检查单个元素:

.L2:
        vandps  (%rax), %ymm1, %ymm1
        vmovdqa %xmm1, %xmm0
        addq    $32, %rax
        vmovd   %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vpextrd $1, %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vpextrd $2, %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vpextrd $3, %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vextractf128    $0x1, %ymm1, %xmm0
        vmovd   %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vpextrd $1, %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vpextrd $2, %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vpextrd $3, %xmm0, %edx
        testl   %edx, %edx
        jne     .L2
        vzeroupper
        ret

有什么方法可以让 GCC 为其生成有效的测试,而无需恢复使用内在函数?

更新:作为参考,使用 (V)PTEST 内置的不可移植 GCC 的代码:

typedef uint32_t v8ui __attribute__ ((vector_size (32)));
typedef long long int v4si __attribute__ ((vector_size (32)));

const v8ui ones = { 1, 1, 1, 1, 1, 1, 1, 1 };

v8ui*
foo(v8ui *mem) {
    v8ui v;
    for ( v = ones;
          !__builtin_ia32_ptestz256((v4si)v,
                                    (v4si)ones);
          mem++)
        v &= *(mem);

    return mem;
}

【问题讨论】:

  • 没有办法让 gcc 使用几乎任何指令,如果你确实找到了办法,它可能不适用于其他优化级别或其他版本的 gcc。更糟糕的是,欺骗编译器发出特定指令实际上将您的程序归类为仅在单个编译器上工作(性能方面)。这真的比内在函数或 asm 更便携吗?
  • @SteveCox:同样,这些都是无副作用的测试。是否生成使它们短路的代码取决于编译器。它甚至可能重新排序!
  • 显然是错误的。编译器没有重新排序这些测试的余地。 v[0]==0 可能暗示 v+1 是一个无效的内存地址,因为我不知道 C 字符串。 open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf 阅读 6.5.14(特别是声明 4)
  • @SamuelEdwinWard:这里的v 不是数组或指针。见GCC Vector Extensions

标签: c gcc vectorization sse avx2


【解决方案1】:

vptest 没有帮助吗?如果您正在关注性能,有时您会对本机类型可以提供的功能感到惊讶。这是一些使用 vanilla memcmp() 以及 vptest 指令(通过相应的内在函数使用)的代码。我没有计时。

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <immintrin.h>

typedef uint32_t v8ui __attribute__ ((vector_size (32)));

v8ui*
foo1(v8ui *mem)
{   
    v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };

    if (memcmp(mem, &v, sizeof (v8ui)) == 0) {
            printf("Ones\n");
    } else {
            printf("NOT Ones\n");
    }

    return mem;
}

v8ui*
foo2(v8ui *mem)
{   
    v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
    __m256i a, b;

    a = _mm256_loadu_si256((__m256i *)(&v));
    b = _mm256_loadu_si256((__m256i *)(&mem));

    if (!_mm256_testz_si256(a, b)) {
            printf("NOT Ones\n");
    } else {
            printf("Ones\n");
    }

    return mem;
}

int
main()
{
    v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
    foo1(&v);
    foo2(&v);
}

编译标志:

gcc -mavx2 foo.c

哇!直到现在我才看到您想让 GCC 在不使用内在函数的情况下生成 vptest 指令。无论如何我都会留下代码。

【讨论】:

    【解决方案2】:

    如果编译器的优化不足以自动产生优化,您有以下三种选择:

    • 获取新的编译器。
    • 手动进行优化(例如,使用内在函数,例如在您的测试和其他答案中)。
    • 修改编译器以自动产生优化。

    您已经通过使用 gcc 扩展自动排除了第一个选项,尽管 llvm/clang 可能会为您扩展这些扩展。

    你已经公然排除了第二个选项。

    对我来说,第三个选项似乎是您的最佳选择。 gcc 是开源的,因此您可以对它进行(并提交)您自己的更改。如果您可以修改 gcc 以自动产生这种优化(最好是 100% 标准 C),那么您不仅可以实现产生这种优化的目标,而无需在程序中引入 crud,而且您还可以节省无数的手动优化(尤其是将来会锁定您使用特定编译器的非标准编译器)。

    【讨论】:

      【解决方案3】:

      gcc 4.9.2 -O3 -mavx2(在 64 位模式下)没有意识到它可以使用 ptest|||

      | 版本用vmovdvpextrd 提取向量元素,并在32 位寄存器之间用7 个or insns 组合事物。所以这很糟糕,并且没有利用任何仍然会产生相同逻辑真值的简化。

      || 版本同样糟糕,一次提取元素的方法相同,但每个元素都提取一个 test / jne

      因此,在这一点上,您不能指望 GCC 能够识别这样的测试并执行任何远程高效的操作。 (pcmpeq / movmsk / test 是另一个不错的序列,但 gcc 也不会生成它。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-08
        • 2013-09-26
        • 1970-01-01
        • 2015-01-03
        • 2017-01-07
        • 2020-07-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多