簡體   English   中英

使用向量擴展時讓GCC生成PTEST指令

[英]Getting GCC to generate a PTEST instruction when using vector extensions

當使用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條件的測試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 4.9.2 -O3 -mavx2 (在64位模式下)沒有意識到它可以使用ptest ,使用|| 或者|

| version使用vmovdvpextrd提取向量元素,並將內容與7位or 32位寄存器之間的insn組合在一起。 所以它非常糟糕,並沒有利用任何仍然會產生相同邏輯真值的簡化。

|| 版本同樣糟糕,並且每次都提取相同的元素,但每個元素都會執行一次test / jne

所以在這一點上,你不能指望GCC識別這樣的測試並做任何遠程高效的事情。 pcmpeq / movmsk / test是另一個不錯的序列,但是gcc也不生成它。)

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指令。 無論如何我都會留下代碼。

如果編譯器不夠自動以自動生成優化,則有三個選項:

  • 獲取一個新的編譯器。
  • 手動生成優化(例如,使用諸如測試和其他答案中的內在函數)。
  • 修改編譯器以自動生成優化。

您已經通過使用gcc擴展自動排除了第一個選項,盡管llvm / clang可能會為您擴展這些擴展。

你已經公然排除了第二種選擇。

第三種選擇似乎是我最好的選擇。 gcc是開源的,因此您可以對其進行(並提交)自己的更改。 如果您可以修改gcc以自動生成此優化(理想情況下來自100%標准C),那么您不僅可以實現產生此優化的目標,而且不會將crud引入您的程序,但您還將節省無數的手動優化(尤其是將來鎖定您使用特定編譯器的非標准版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM