Comment puis-je écrire une version portable de vecteurs intégrés GNU C , qui ne dépend pas de l’insortingnsèque x86 set1?
typedef uint16_t v8su __atsortingbute__((vector_size(16))); v8su set1_u16_x86(uint16_t scalar) { return (v8su)_mm_set1_epi16(scalar); // cast needed for gcc }
Il doit sûrement y avoir un meilleur moyen que
v8su set1_u16(uint16_t s) { return (v8su){s,s,s,s, s,s,s,s}; }
Je ne veux pas écrire une version AVX2 de celle-ci pour diffuser un seul octet!
Même une réponse gcc only ou clang only à cette partie serait intéressante , dans les cas où vous souhaitez affecter une variable au lieu de n’utiliser que comme opérande d’un opérateur binary (ce qui fonctionne bien avec gcc, voir ci-dessous).
Si je veux utiliser un broadcast-scalar comme un opérande d’un opérateur binary, cela fonctionne avec gcc ( comme indiqué dans le manuel ), mais pas avec clang:
v8su vecdiv10(v8su v) { return v / 10; } // doesn't comstack with clang
Avec clang, si je ne cible que x86 et n’utilise que la syntaxe vectorielle native pour que le compilateur génère des instructions et des constantes inverses multiplicatives modulaires , je peux écrire:
v8su vecdiv_set1(v8su v) { return v / (v8su)_mm_set1_epi16(10); // gcc needs the cast }
Mais alors je dois changer l’insortingnsèque si _mm256_set1_epi16
le vecteur (en _mm256_set1_epi16
), au lieu de convertir le code entier en AVX2 en passant à vector_size(32)
en un seul endroit (pour un SIMD vertical pur qui n’a pas besoin d’être mélangé). Cela va également à l’encontre d’une partie de l’objective des vecteurs natifs, car cela ne comstackra pas pour ARM ni pour aucune cible non x86.
La dissortingbution laide est requirejse car gcc, contrairement à Clang, ne considère pas v8us {aka __vector(8) short unsigned int}
compatible avec __m128i {aka __vector(2) long long int}
.
BTW, tout cela comstack au mieux avec gcc et clang ( voir sur Godbolt ). Il s’agit simplement d’écrire avec élégance, avec une syntaxe lisible qui ne répète pas les N fois scalaires . Par exemple, v / 10
est suffisamment compact pour qu’il ne soit même pas nécessaire de le mettre dans sa propre fonction.
Comstackr efficacement avec ICC est un bonus, mais pas obligatoire. Les vecteurs natifs de GNU C sont clairement une reflection après coup pour ICC, et même des choses simples comme celle-ci ne comstacknt pas efficacement . set1_u16
comstack dans 8 magasins scalaires et une charge vectorielle, au lieu de MOVD / VPBROADCASTW (avec -xHOST
activé, car il ne reconnaît pas -march=haswell
, mais Godbolt s’exécute sur un serveur pris en charge par AVX2). _mm_
purement les résultats de _mm_
insortingnsics est _mm_
, mais la division appelle une fonction SVML!
Une solution de diffusion générique peut être trouvée pour GCC et Clang en utilisant deux observations
scalar - vector
. x - 0 = x
( mais x + 0
ne fonctionne pas car zéro est signé ). Voici une solution pour un vecteur de quatre flotteurs.
#if defined (__clang__) typedef float v4sf __atsortingbute__((ext_vector_type(4))); #else typedef float v4sf __atsortingbute__ ((vector_size (16))); #endif v4sf broadcast4f(float x) { return x - (v4sf){}; }
La même solution générique peut être utilisée pour différents vecteurs. Voici un exemple pour un vecteur de huit courts métrages non signés.
#if defined (__clang__) typedef unsigned short v8su __atsortingbute__((ext_vector_type(8))); #else typedef unsigned short v8su __atsortingbute__((vector_size(16))); #endif v8su broadcast8us(short x) { return x - (v8su){}; }
ICC (17) prend en charge un sous-ensemble des extensions vectorielles GCC, mais ne prend en charge ni vector + scalar
ni vector*scalar
, de sorte que les éléments insortingnsèques sont toujours nécessaires pour les diffusions. MSVC ne prend en charge aucune extension vectorielle.