2015-11-12 2 views
6

Je cherche une instruction SSE qui prend deux arguments de quatre entiers de 32 bits dans __m128i, calcule la somme des paires correspondantes et renvoie le résultat sous forme de deux entiers de 64 bits dans __m128i.Instruction SSE pour additionner des entiers de 32 bits à 64 bits

Existe-t-il une instruction pour cela?

+0

[Voici une solution pour 64bit à 128bit pour SSE, SSE + XOP, AVX2, AVX512] (http://stackoverflow.com/questions/27923192/practical-bignum-avx-sse-possible/27978043#27978043) . –

+0

Pourquoi voulez-vous faire cela? Je comprends pourquoi vous voudriez 64b + 64b + carry mais pas 32b + 32b + carry. –

Répondre

6

Il n'y a pas d'opérations SSE avec report. Pour ce faire, vous devez d'abord décompresser les entiers 32 bits (punpckldq/punpckhdq) en 4 groupes d'entiers 64 bits en utilisant un vecteur auxiliaire tous les zéros, puis utiliser l'addition par paires 64 bits.

+3

SSE4.1 a quelques instructions d'élargissement d'entier qui le rend un peu plus facile et plus rapide. – Mysticial

+1

@Mysticial: Pour les entiers signés, c'est en fait un * lot * plus facile et plus rapide avec 'pmovsx'. Ce n'est pas aussi grand que je le pensais au début, puisque j'avais une idée assez bonne en écrivant ma réponse pour le déballage avec un masque, au lieu de déballer et * ensuite * de mélanger un masque de signe. Mais 'pmovsx' est très agréable si vous chargez de la mémoire, sinon vous devez travailler pour que la moitié supérieure soit décalée pour se préparer à l'extension de signe. –

2

SSE a seulement ceci pour byte-> word et word-> dword. (pmaddubsw (SSSE3) et pmaddwd (MMX/SSE2), qui multiplient verticalement v1 * v2, puis ajoutent horizontalement les paires voisines.)

Je ne sais pas exactement ce que vous voulez que les sorties soient. Vous avez 8 entiers d'entrée (deux vecteurs de 4) et 2 entiers de sortie (un vecteur de deux). Comme il n'y a pas d'insn qui effectue une addition de vecteurs 32 + 32 -> 64b, regardons simplement comment étendre ou étendre les deux éléments 32b d'un vecteur à 64b. Vous pouvez combiner ceci dans tout ce dont vous avez besoin, mais gardez à l'esprit qu'il n'y a pas de paires additionnelles-horizontales phaddq, seulement vertical paddq.

phaddd est similaire à ce que vous voulez, mais sans l'élargissement: la moitié inférieure du résultat est la somme des paires horizontales dans le premier opérande, la moitié supérieure est la somme des paires horizontales dans le deuxième opérande. Cela vaut à peine la peine d'utiliser si vous avez besoin de tous ces résultats, et vous n'allez pas les combiner davantage. (c'est-à-dire qu'il est généralement plus rapide de mélanger et d'ajouter verticalement au lieu de faire phadd pour additionner horizontalement un accumulateur de vecteur à la fin d'une réduction.) Si vous voulez tout résumer à un résultat, faites des sommes verticales normales jusqu'à ce que vous tombiez à un registre.) phadddpourrait être mis en œuvre dans le matériel pour être aussi rapide que paddd (latence de cycle unique et le débit), mais il n'est pas dans toute CPU AMD ou Intel.


Comme Mysticial a commenté, SSE4.1 pmovzxdq/pmovsxdq sont exactement ce dont vous avez besoin, et peut même le faire à la volée dans le cadre d'une charge à partir d'un emplacement de mémoire 64b (contenant deux entiers 32b).

SSE4.1 a été introduit avec Intel Penryn, 2ème génération Core2 (45nm die shrink core2), la génération avant Nehalem. Retomber sur un chemin de code non-vectoriel sur des processeurs plus anciens que cela pourrait convenir, en fonction de combien vous vous souciez de ne pas être lent sur les processeurs qui sont déjà vieux et lent.


Sans SSE4.1:

zéro extension Unsigned est facile. Comme réponse pmdj, il suffit d'utiliser punpck* lo et salut pour déballer avec zéro.

Si vos entiers sont signés, vous devrez faire l'extension de signe manuellement.

Il n'y a pas psraq, seulement psrad (Dword arithmétique arithmétique à droite) et psraw. S'il y en avait, vous pourriez déballer avec lui-même et ensuite effectuer un décalage arithmétique de 32b. Au lieu de cela, nous avons probablement besoin de générer un vecteur où chaque élément est transformé en son bit de signe. Mélangez ensuite cela avec un vecteur non compressé (mais pblendw est SSE4.1 aussi, donc nous devions utiliser por). Ou mieux, déballez le vecteur original avec un vecteur de signes-masques.

# input in xmm0 
movdqa xmm1, xmm0 
movdqa xmm2, xmm0 
psrad  xmm0, 31  ; xmm0 = all-ones or all-zeros depending on sign of input elements. xmm1=orig ; xmm2=orig 
         ; xmm0 = signmask; xmm1=orig ; xmm2=orig 
punpckldq xmm1, xmm0 ; xmm1 = sign-extend(lo64(orig)) 
punpckhdq xmm2, xmm0 ; xmm2 = sign-extend(hi64(orig)) 

Cela devrait fonctionner avec une latence de 2 cycles pour les deux résultats sur Intel SnB ou IvB. Haswell et plus tard ont seulement un port de shuffle (donc ils ne peuvent pas faire les deux punpck insns en parallèle), ainsi xmm2 sera retardé pour un autre cycle là. Les processeurs Intel pré-SnB gèlent généralement sur le frontend (décodeurs, etc) avec des instructions vectorielles, car ils font souvent plus de 4B par insn.

Déplacement de l'original au lieu de la copie raccourcit la chaîne de dépendance pour tout produit xmm0, pour les CPU sans élimination de déplacement (manipulation mov instructions à l'étape de registre de renommage, ils sont donc sans latence. Intel, et seulement sur IvB et plus tard.) Avec les instructions AVX à 3 opérandes, vous n'avez pas besoin du movdqa, ou du 3e registre, mais vous pouvez simplement utiliser vpmovsx pour le low64 de toute façon. Pour vous inscrire étendre le haut 64, vous auriez probablement psrldq octets déplacer le haut 64 jusqu'à la faible 64.

Ou movhlps ou punpckhqdq self,self à utiliser une instruction plus courte à encode. (Ou AVX2 vpmovsx à un 256b reg, puis vextracti128 128 supérieure, pour obtenir les deux 128b résultats avec seulement deux instructions.)


Contrairement équipes GP-registre (par exemple sar eax, 31), les changements de vecteur saturent le comte à la place de masquage. Laissant le bit de signe original comme le LSB (décalage de 31) au lieu d'une copie de celui-ci (déplacement de 32) fonctionne bien, aussi. Il a l'avantage de ne pas nécessiter un gros commentaire avec le code expliquant cela pour les personnes qui s'inquiéteraient quand elles verront psrad xmm0, 32.