2012-04-19 4 views
8

Pour un projet de loisir sur lequel je travaille, j'ai besoin d'émuler certaines opérations entières de 64 bits sur un processeur x86, et il doit être rapide.Émulation SSE optimisée d'entiers 64 bits

Actuellement, je fais cela via des instructions MMX, mais ce qui est vraiment une douleur à travailler avec, parce que je dois vider l'état du registre fp tout le temps (et parce que la plupart des instructions MMX traitent signé entiers, et J'ai besoin d'un comportement non signé). Donc, je me demande si les gourous SSE/optimisation ici sur SO peuvent arriver à une meilleure mise en œuvre en utilisant SSE.

Les opérations dont j'ai besoin sont les suivants (tout à fait spécifiques):

uint64_t X, Y; 

X = 0; 
X = 1; 
X << 1; 
X != Y; 
X + 1; 
X & 0x1 // get lsb 
X | 0x1 // set lsb 
X > Y; 

Plus précisément, je ne ont pas besoin plus d'usage général ou le déplacement, par exemple, il suffit d'ajouter un et à gauche un quart de travail. Vraiment, juste les exact opérations montrées ici. Sauf, bien sûr, sur x86, uint64_t est émulé en utilisant deux scalaires 32 bits, ce qui est lent (et, dans mon cas, ne fonctionne tout simplement pas, car j'ai besoin de charges/magasins pour être atomique, qui ils ne le seront pas lors du chargement/stockage de deux registres séparés).

Par conséquent, j'ai besoin d'une solution SIMD. Certaines de ces opérations sont triviales, supportées par SSE2 déjà. D'autres (!= et <) nécessitent un peu plus de travail.

Suggestions? SSE et SSE2 vont bien.Il avait pris un peu de persuasion pour permettre SSE3 et SSE4 est probablement hors de question (A CPU qui prend en charge SSE4 est susceptible de fonctionner 64 bits de toute façon, et donc je ne pas besoin de ces solutions de contournement)

+0

L'addition de 64 bits est directement prise en charge dans SSE2. Je suppose que vous avez également besoin de multiplications 64 bits? 64 x 64 -> 64 bits (moitié inférieure), ou avez-vous besoin de 64 x 64 -> 128 bits? – Mysticial

+0

Multiplier n'est pas nécessaire, juste les opérations spécifiques que j'ai montrées ci-dessus (donc pas même l'addition générale, juste incrément de 1. Et oui, l'addition est fournie par SSE2, mais je pensais que je pourrais aussi montrer toutes les opérations Pour être complet, cela signifie que certains d'entre eux sont faciles :) – jalf

+0

À quoi voulez-vous que les opérateurs logiques soient édités? Registre général? ou SSE? – Mysticial

Répondre

14

SSE2 a un appui direct pour certaines opérations sur les entiers de 64 bits:

Régler les deux éléments à 0:

__m128i z = _mm_setzero_si128(); 

régler les deux éléments à 1:

__m128i z = _mm_set_epi32(0,1,0,1); 

ajouter Verticalement/soustraire chaque entier de 64 bits:

__m128i z = _mm_add_epi64(x,y) 
__m128i z = _mm_sub_epi64(x,y) 

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_arithmetic.htm#intref_sse2_integer_arithmetic

Maj gauche:

__m128i z = _mm_slli_epi64(x,i) // i must be an immediate 

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_int_shift.htm

opérateurs sur mot

__m128i z = _mm_and_si128(x,y) 
__m128i z = _mm_or_si128(x,y) 

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_logical.htm

SSE n'ont pas incréments, de sorte que vous devrez utiliser une constante avec 1.


Les comparaisons sont plus difficiles car il n'existe aucun support 64 bits.

est ici celui pour l'égalité:

__m128i t = _mm_cmpeq_epi32(a,b); 
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177)); 

Ceci règlera le chaque élément 64 bits à 0xffffffffffff si elles sont égales. Si vous le voulez comme 0 ou 1 dans un int, vous pouvez le retirer en utilisant _mm_cvtsi32_si128() et ajouter 1.

et moins Than: (non vérifié)

a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000)); 
b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000)); 
__m128i t = _mm_cmplt_epi32(a,b); 
__m128i u = _mm_cmpgt_epi32(a,b); 
__m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177)); 
z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z); 

Ceci règlera le chaque élément 64 bits 0xffffffffffff si l'élément correspondant a est inférieure à b.


Voici les versions de «égal à» et «inférieur à» qui retournent un booléen. Ils renvoient le résultat de la comparaison pour l'entier inférieur de 64 bits.

inline bool equals(__m128i a,__m128i b){ 
    __m128i t = _mm_cmpeq_epi32(a,b); 
    __m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177)); 
    return _mm_cvtsi128_si32(z) & 1; 
} 
inline bool lessthan(__m128i a,__m128i b){ 
    a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000)); 
    b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000)); 
    __m128i t = _mm_cmplt_epi32(a,b); 
    __m128i u = _mm_cmpgt_epi32(a,b); 
    __m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177)); 
    z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z); 
    return _mm_cvtsi128_si32(z) & 1; 
} 
+0

Je pense que mon "moins-que" cas est faux. Je vais re-vérifier ... – Mysticial

+0

Je viens de mettre à jour le code "inférieur à". Pas sûr à 100% si c'est correct. – Mysticial

+0

J'ai écrit un test rapide de la "moins de" cas [ici] (http://ideone.com/U4LXd) pour VS2010. Peut-être besoin d'un peu de peaufinage pour fonctionner sur d'autres compilateurs. Il échoue pour '0x00000000 <0x00000001' (sauf si j'ai fait une erreur dans le test) – jalf