Je suis en train de migrer du code vectorisé écrit à l'aide de SSE2 intrinsics sur les intrinsions AVX2. P>
beaucoup à ma déception, je découvre que les instructions de décalage _mm256_slli_si256 strong> et _mm256_srli_si256 strong> 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 Pouvez-vous me recommander un court substitut? P>
mise à jour: p>
ou p>
pour les déplacements supérieurs à 16 octets. P>
Mais la question reste pour _mm256_slli_si256 code> est efficacement obtenu avec p>
_mm256_alignr_epi8 (a, _mm256_permute2x128_si256 (a, a, _mm_shuffle (0, 0, 3, 0)), n) code> p>
_mm256_slli_si256 (_mm256_permute2x128_si256 (a, a, _mm_shuffle (0, 0, 3, 0)), n) code> p>
_mm256_srli_si256 code>. p>
3 Réponses :
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; } //----------------------------------------------------------------------------
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.
de différentes entrées, j'ai rassemblé ces solutions. La clé pour traverser la barrière inter-voies est l'instruction Align, 0 _mm256_alignr_epi8 code>.
_mm256_slli_si256 (a, n) h2>
_mm256_srli_si256(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), N - 16)
La clé pour traverser la barrière inter-voies est _mm256_permute2x128_si256 code>, 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 code> est plus lent que
_mm256_permute4x64_epi64 code>
pour permerger des voies d'un seul vecteur comme vous faites ici.
@Petercordes: significativement?
Oui, sur Ryzen vperm2i128 code> est 8 UOPS, LAT = 3 TPT = 3.
vpermq code> est 3 UOPS, LAT = 2, TPT = 2. (Celles-ci sont réellement pour les équivalents FP,
vperm2f128 code> et
vpermpd code>, puisque le brouillard Agner a omis beaucoup de choses entier AVX2 pour Ryzen). Sur KNL,
vpermq code> 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 code> est toujours au moins aussi bon que
vperm2i128 code> pour shuffling dans un seul vecteur. De plus, il peut plier une charge en tant qu'opérande de source de mémoire.
Si le nombre de décalages est un multiple de 4 octets, 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 Cette réponse est assez minimale, car vpermd code> (
_mm256_permutevar8x32_épi32 code>) 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). P>
0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 code> ou quelque chose, sauf que
0 code> 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 . P>
vpermd code> 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. P>
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 i>, 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 b>. 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 code>. Malheureusement, il n'y a pas de
_mm256_alignl_epi8 code> correspondance.
Vous n'avez pas besoin
_mm256_alignl_epi8 code> (c'est pourquoi il n'y a pas d'instruction ni intrinsèque pour cela) -
_mm256_alignr_epi8 code> 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.