12
votes

Émoulement des changements sur 32 octets avec AVX

Je suis en train de migrer du code vectorisé écrit à l'aide de SSE2 intrinsics sur les intrinsions AVX2.

beaucoup à ma déception, je découvre que les instructions de décalage _mm256_slli_si256 et _mm256_srli_si256 ne fonctionnent que sur les deux moitiés des registres AVX séparément et les zéros sont introduits entre les deux. (Ceci est en contraste avec _mm_slli_si128 et _mm_srli_si128 qui gère des registres de SSE entiers.)

Pouvez-vous me recommander un court substitut?

mise à jour:

_mm256_slli_si256 est efficacement obtenu avec

_mm256_alignr_epi8 (a, _mm256_permute2x128_si256 (a, a, _mm_shuffle (0, 0, 3, 0)), n)

ou

_mm256_slli_si256 (_mm256_permute2x128_si256 (a, a, _mm_shuffle (0, 0, 3, 0)), n)

pour les déplacements supérieurs à 16 octets.

Mais la question reste pour _mm256_srli_si256 .


12 commentaires

Que diriez-vous de nous rappeler ce que ces instructions SLLI font, voire mieux ce que vous voulez faire exactement? Avez-vous regardé le code généré par GCC avec __builtin_shauffe ou clang avec sa propre syntaxe?


Et que voulez-vous dire par "seulement la moitié supérieure" "Le reste est zeré"? Ce n'est pas ce que dit Intel's Doc.


La raison pour laquelle il n'y a pas de décalage de 32 octets est que le matériel ne peut tout simplement pas le faire. Le matériel est SIMD et un changement de vecteur complet n'est pas simd. Si vous constatez que vous avez besoin de telles instructions, cela pourrait valoir la peine de réexaminer la conception. Vous essayez probablement de faire quelque chose de non-SIMD à l'aide de SIMD qui conduit souvent à une avalanche d'autres problèmes (performances) également. Si c'est une question de désalignement, utilisez simplement un accès à la mémoire mal alignée. Sur HASWELL, l'accès mal aligné est presque aussi rapide que l'accès aligné.


@MARC GLISSE: "Les octets à faible commande vide sont effacés (défini sur tous les" 0 ') ". logiciel.intel.com/sites/products/documentation/doclib/iss/2 013 / ...


@ Mysticial: Comme écrit dans mon message, le SSE _mm_slli_si128 effectue un changement complet. Et donc PSRLQ / PSLLQ dans "Old" MMX. Je suppose que la mise en œuvre d'un vélecteur complet de 256 bits est trop demandée. Je travaille sur les fonctions de traitement d'images de quartier, qui sont intrinsèquement mixtes-alignées.


@Yvesdaoust Je crois que vous êtes mal interprété cette doc. Dans Chaque moitié de 128 bits , les données sont déplacées vers la gauche et 0s sont utilisées pour remplir l'espace vide à droite. "L'ordre bas" doit être compris comme à l'intérieur de la voie 128 bits . Il ne nulle pas une voie entière. À propos, HTML HTML de Intel du compilateur intrinsique suce, il est souvent illisible ou faux, la référence de l'instruction PDF est beaucoup plus utile.


@MARC GLISSE: C'est vrai, je mette à jour la question. Le problème reste, de toute façon, car certains des octets sont abandonnés.


@Paul R: Ma question n'est pas une duplication car elle contient des quarts de gauche et de droite. Le précédent ne résout que le cas d'un décalage de gauche très efficacement avec une instruction _mm256_alignr_epi8 . Malheureusement, il n'y a pas de _mm256_alignl_epi8 correspondance.


Vous n'avez pas besoin _mm256_alignl_epi8 (c'est pourquoi il n'y a pas d'instruction ni intrinsèque pour cela) - _mm256_alignr_epi8 fonctionne pour les cas de défilement gauche et droit (changer simplement les arguments et ajuster la valeur de décalage).


Si vous rouvrez la question, je peux fournir une solution complète.


@Yvesdaoust: OK - Vote de re-Ouvrir, mais idéalement, cette question doit être fusionnée avec son plus tôt Doppelgänger .


Lors de la migration de 128 bits SIMD vers AVX-256, il est généralement plus facile de penser au problème en termes de deux opérations collées ensemble de 128 bits, au lieu d'une opération globale de 256 bits. Pas toujours idéal, mais facilite les traduire un clapcheau et fonctionne généralement mieux que de la chaussant avec des permutations.


3 Réponses :


5
votes

Voici une fonction de changement de bit à gauche d'un registre YMM à l'aide d'AVX2. Je l'utilise pour changer de gauche par un, bien qu'il semble que cela fonctionne pour des changements jusqu'à 63 bits.

//----------------------------------------------------------------------------
// bit shift left a 256-bit value using ymm registers
//          __m256i *data - data to shift
//          int count     - number of bits to shift
// return:  __m256i       - carry out bit(s)

static __m256i bitShiftLeft256ymm (__m256i *data, int count)
   {
   __m256i innerCarry, carryOut, rotate;

   innerCarry = _mm256_srli_epi64 (*data, 64 - count);                        // carry outs in bit 0 of each qword
   rotate     = _mm256_permute4x64_epi64 (innerCarry, 0x93);                  // rotate ymm left 64 bits
   innerCarry = _mm256_blend_epi32 (_mm256_setzero_si256 (), rotate, 0xFC);   // clear lower qword
   *data      = _mm256_slli_epi64 (*data, count);                             // shift all qwords left
   *data      = _mm256_or_si256 (*data, innerCarry);                          // propagate carrys from low qwords
   carryOut   = _mm256_xor_si256 (innerCarry, rotate);                        // clear all except lower qword
   return carryOut;
   }

//----------------------------------------------------------------------------


2 commentaires

Intéressant. Six instructions sont encore beaucoup. Je ne cherche que des changements d'octets.


Pour les changements d'octets, 4 instructions doivent faire: décalage à gauche, passer à droite, apporter une voie inférieure ou.



8
votes

de différentes entrées, j'ai rassemblé ces solutions. La clé pour traverser la barrière inter-voies est l'instruction Align, _mm256_alignr_epi8 code>.

_mm256_slli_si256 (a, n) h2>

0

_mm256_srli_si256(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), N - 16)


5 commentaires

La clé pour traverser la barrière inter-voies est _mm256_permute2x128_si256 , sûrement?


Non, je veux dire effectuer une opération qui assemble d'octets de deux voies différentes. En tant que DOC, le processeur crée un "composite 32 octets" avant de changer de vitesse. Le permanence gère des voies entières.


Sur Ryzen et KNL, _mm256_permute2x128_si256 est plus lent que _mm256_permute4x64_epi64 pour permerger des voies d'un seul vecteur comme vous faites ici.


@Petercordes: significativement?


Oui, sur Ryzen vperm2i128 est 8 UOPS, LAT = 3 TPT = 3. vpermq est 3 UOPS, LAT = 2, TPT = 2. (Celles-ci sont réellement pour les équivalents FP, vperm2f128 et vpermpd , puisque le brouillard Agner a omis beaucoup de choses entier AVX2 pour Ryzen). Sur KNL, vpermq a deux fois le débit et la latence inférieure 1C. Il n'y a pas d'inconvénient sur aucun CPU, Afaik; vpermq est toujours au moins aussi bon que vperm2i128 pour shuffling dans un seul vecteur. De plus, il peut plier une charge en tant qu'opérande de source de mémoire.



1
votes

Si le nombre de décalages est un multiple de 4 octets, vpermd ( _mm256_permutevar8x32_épi32 ) avec le masque de shuffle droit fera l'affaire avec une seule instruction (ou plus, si vous effectivement besoin de zéro les octets décalés au lieu de copier un élément différent sur eux).

Pour prendre en charge les comptes de décalage variable (multiple-4b), vous pouvez charger le masque de commande d'une fenêtre dans une matrice de 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 ou quelque chose, sauf que 0 est juste l'élément inférieur, et ne peut pas zéro chose. Pour plus d'informations sur cette idée de générer un masque à partir d'une fenêtre coulissante, voir Ma réponse sur une autre question .

Cette réponse est assez minimale, car vpermd ne résout pas directement le problème. Je le souligne comme une alternative qui pourrait travailler dans certains cas où vous recherchez un décalage de vecteur complet.


0 commentaires