2015-04-06 9 views
6

Podczas korzystania z rozszerzeń wektorów GCC dla C, jak mogę sprawdzić, czy wszystkie wartości na wektorze są równe zero?Uzyskiwanie instrukcji GCC w celu wygenerowania instrukcji PTEST podczas korzystania z rozszerzeń wektorów

Na przykład:

#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 ma dyspozycję PTEST który pozwala uruchomić test podobny do tego stosowanego jako warunek for ale kod generowany przez GCC prostu rozpakowuje wektor i sprawdza pojedyncze elementy jeden po drugim:

.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 

Czy istnieje sposób, aby GCC wygenerować skuteczny test na to bez powrotu do korzystania z wewnętrznej?

Aktualizacja: odsyłającym kodu przy użyciu wbudowanych nieprzenośne GCC dla (V)PTEST:

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; 
} 
+1

Nie ma sposobu, aby GCC użył prawie wszystkich instrukcji, a jeśli znajdziesz sposób, prawdopodobnie nie będzie działać na innych poziomach optymalizacji lub innych wersjach gcc. co gorsza, wyłudzanie kompilatora w celu wysłania konkretnej instrukcji zasadniczo powoduje, że twój program działa tylko (pod względem wydajności) na jednym kompilatorze. czy to naprawdę jest bardziej przenośne niż wewnętrzne lub asm? –

+0

również z wiadomością, ptest nigdy nie będzie równoznaczny z v [0] || v [1] || v [2] || v [3] || v [4] || v [5] || v [6] || v [7], ponieważ ocena zwarcia wymaga skoku po każdym indywidualnym wyrażeniu boolowskim –

+0

@SteveCox, może moje sformułowanie nie było jasne, moim celem jest przekonanie GCC do wygenerowania wydajnego kodu maszynowego. PTEST to tylko jeden ze sposobów. – salva

Odpowiedz

1

Would not vptest pomóc? Jeśli patrzysz na wydajność, czasami będziesz zaskoczony tym, co może zapewnić typ natywny. Oto kod, który używa wanilii memcmp(), a także instrukcji vptest (stosowanej poprzez odpowiednie wewnętrzne). Nie traciłem czasu na funkcje.

#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); 
} 

Skompiluj flagi:

gcc -mavx2 foo.c

Doh! Dopiero teraz zobaczyłem, że chciałeś, aby GCC wygenerował najwolniejsze instrukcje bez korzystania z intrinsics. I tak zostawiam kod.

0

Jeśli kompilator nie wystarcza do wytworzenia optymalnego optymalizacji automatycznie, masz trzy opcje:

  • dostać nowy kompilator.
  • Wykonaj ręczną optymalizację (np. Za pomocą właściwości wewnętrznych, takich jak w teście i drugiej odpowiedzi).
  • Zmodyfikuj kompilator, aby automatycznie uzyskać optymalizację.

Całkowicie wykluczyłeś pierwszą opcję automatycznie, korzystając z rozszerzeń gcc, choć llvm/clang może rozszerzyć te rozszerzenia.

Wykluczyłeś drugą opcję całkiem rażąco.

Trzecia opcja wydaje mi się najlepszą opcją. gcc jest open source, więc możesz zrobić (i zatwierdzić) własne zmiany do niego. Jeśli możesz zmodyfikować gcc, by automatycznie przeprowadzić optymalizację (najlepiej ze 100% standardowego C), to nie tylko osiągniesz swój cel polegający na stworzeniu tej optymalizacji bez wprowadzania crud do twojego programu, ale także zaoszczędzisz niezliczoną liczbę ręcznych optymalizacji (szczególnie te niestandardowe, które blokują ci użycie określonego kompilatora) w przyszłości.

2

gcc 4.9.2 -O3 -mavx2 (w trybie 64-bitowym), nie zdawał sobie sprawy, to może użyć ptest do tego, albo || lub |.

Wersja | wyodrębnia elementy wektorowe z vmovd i vpextrd i łączy rzeczy z 7 or insns między rejestrami 32bit. Jest więc dość zły i nie wykorzystuje żadnych uproszczeń, które nadal będą generować tę samą wartość logiczną.

Wersja || jest równie zła, i robi ten sam ekstrakt w jednym elemencie, ale ma dla każdego z nich tylko jeden element-wyodrębnienia.

W tym momencie nie można liczyć na to, że GCC rozpoznaje takie testy i robi coś zdalnie wydajnego. (pcmpeq/movmsk/test to kolejna sekwencja, która nie byłaby zła, ale gcc też jej nie generuje.)

Powiązane problemy