2016-11-21 16 views
6
x86,

Jak napisać wersję przenośną, która nie jest zależna od wewnętrznego zestawu x861?Wektory rodzime GNU C: jak emitować skalar, jak _mm_set1_epi16,

typedef uint16_t v8su __attribute__((vector_size(16))); 

v8su set1_u16_x86(uint16_t scalar) { 
    return (v8su)_mm_set1_epi16(scalar); // cast needed for gcc 
} 

pewnością nie musi być lepszy sposób niż

v8su set1_u16(uint16_t s) { 
    return (v8su){s,s,s,s, s,s,s,s}; 
} 

Nie chcę napisać wersję AVX2 tego za nadawanie jednego bajta!

Nawet gcc-only lub dzyń tylko odpowiedzieć na tę część byłaby interesująca, w przypadkach, w których chcesz przypisać do zmiennej zamiast tylko używając jako argumentu do operatora binarnego (który działa dobrze z gcc patrz poniżej).


Jeśli chcę użyć broadcast-skalar jako jeden operand operatora binarnego, to działa z gcc (as documented in the manual), ale nie z brzękiem:

v8su vecdiv10(v8su v) { return v/10; } // doesn't compile with clang 

Z brzękiem, jeśli I” m kierowania tylko x86 i tylko przy użyciu natywnego wektor składni to get the compiler to generate modular multiplicative inverse constants and instructions for me, mogę napisać:

v8su vecdiv_set1(v8su v) { 
    return v/(v8su)_mm_set1_epi16(10); // gcc needs the cast 
} 

Ale potem muszę zmienić wewnętrzna gdybym poszerzyć wektor (do _mm256_set1_epi16), zamiast konwertować cały kod na AVX2, zmieniając go na vector_size(32) w jednym miejscu (dla czysto pionowej karty SIMD, która nie wymaga tasowania). To także pokonuje część celu wektorów natywnych, ponieważ nie będzie to kompilacja dla ARM ani żadnego innego celu niż x86.

Brzydki rzut jest wymagany, ponieważ gcc, w przeciwieństwie do klang, nie uważa v8us {aka __vector(8) short unsigned int} kompatybilny z __m128i {aka __vector(2) long long int}.

BTW, wszystko to kompiluje się do dobrego asm z gcc i clang (see it on Godbolt). To tylko kwestia tego, jak pisać elegancko, z czytelną składnią, która nie powtarza skali N razy. na przykład v/10 jest na tyle kompaktowy, że nie trzeba nawet umieszczać go we własnej funkcji.

Efektywne kompilowanie z ICC to bonus, ale nie jest wymagany. Natywne wektory GNU C są oczywiste dla ICC, a nawet simple stuff like this doesn't compile efficiently. set1_u16 kompiluje do 8 magazynów skalarnych i obciążenia wektorowego zamiast MOVD/VPBROADCASTW (z włączoną -xHOST, ponieważ nie rozpoznaje -march=haswell, ale Godbolt działa na serwerze z obsługą AVX2). Czysto rzucając wyniki wewnątrzinżynierii jest w porządku, ale podział wywołuje funkcję SVML!

+0

Nie można rozsądnie używać wewnętrznej wektorów gcc w clang * tak czy inaczej *, ponieważ tak ochoczo postanowili zaimplementować całkowicie inną semantykę '__bultin_shuffle()'. – EOF

+1

Właśnie znalazłem jakiś stary kod, który napisałem, gdzie pracowałem nad brakującą wewnętrzną transmisją wektorów gcc, wykonując 'vectype v = {0}; v + = typ_skalowy; '. gcc optymalizuje to do transmisji. Nie jest ładny (ponieważ nie może być "const"), ale jest dość krótki. – EOF

Odpowiedz

3

Ogólny rozwiązanie transmisji można znaleźć na GCC i Clang stosując dwie obserwacje

  1. Clang's OpenCL vector extensions i rozszerzenia GCC wektorowe wspierać scalar - vector operacje.
  2. x - 0 = x (but x + 0 does not work due to signed zero).

Oto rozwiązanie dla wektora czterech pływaków.

#if defined (__clang__) 
typedef float v4sf __attribute__((ext_vector_type(4))); 
#else 
typedef float v4sf __attribute__ ((vector_size (16))); 
#endif 

v4sf broadcast4f(float x) { 
    return x - (v4sf){}; 
} 

https://godbolt.org/g/PXr3Xb

samo rozwiązanie ogólna może być wykorzystywane do różnych wektorów. Oto przykład wektora ośmiu niepodpisanych szortów.

#if defined (__clang__) 
typedef unsigned short v8su __attribute__((ext_vector_type(8))); 
#else 
typedef unsigned short v8su __attribute__((vector_size(16))); 
#endif 

v8su broadcast8us(short x) { 
    return x - (v8su){}; 
} 

ICC (17) obsługuje podzbiór GCC rozszerzeń wektorowej, ale nie obsługują vector + scalar lub vector*scalar jeszcze tak intrinsics nadal są niezbędne do transmisji. MSVC nie obsługuje żadnych rozszerzeń wektorowych .

+1

Dodanie do zera może działać dobrze dla wartości całkowitych, ale nie dla float bez '-ffast-matath'. Wypisane zerowe zachowanie (i możliwe podniesienie wyjątków) oznacza, że ​​'x + 0.0' nie może być zoptymalizowane do 'x', więc nieklangowa gałąź ifdef (' zero + x') nie optymalizuje się: [gcc ma 'vaddss' ze stałej skalarnej przed przetasowaniem] (https://godbolt.org/g/US7TSZ). –

+0

Wycofano, ponieważ ta odpowiedź działa dla klang (unika dodawania do zera), ale nie sądzę, że jestem gotów zaakceptować tę odpowiedź. (AFAIK, tam * nie jest * inną odpowiedzią niż ręczne wpisanie tej samej zmiennej do 8 razy w inicjalizatorze, dla v8sf. Prawdopodobnie typy całkowite nadal optymalizują '0 + x' do' x' (jak wzmianki EOF w komentarz), dzięki czemu możemy użyć tego dla wektorów całkowitych i unikać pisania 32 razy dla v32sc). –

+1

@PeterCordes, właśnie naprawiłem to dla GCC! Zamiast robić "zero + x" możesz zrobić "jeden * x"! https://godbolt.org/g/1a39Kv. Zobacz moją zaktualizowaną odpowiedź. –