Question

elma et elmc sont les deux tableaux de unsigned long. Ainsi, sont res1 et res2.

unsigned long simdstore[2];  
__m128i *p, simda, simdb, simdc;  
p = (__m128i *) simdstore;  

for (i = 0; i < _polylen; i++)  
{
    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k++)  
    {
        //res1[i + k] ^= _mulpre1[u1][k];  
        //res2[i + k] ^= _mulpre2[u2][k];               

        simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);  
        simdb = _mm_set_epi64x (res2[i + k], res1[i + k]);  
        simdc = _mm_xor_si128 (simda, simdb);  
        _mm_store_si128 (p, simdc);  
        res1[i + k] = simdstore[0];  
        res2[i + k] = simdstore[1];                     
    }     
}  

Dans la boucle est incluse à la fois la version non SIMD et SIMD de XOR d'éléments. Les deux premières lignes dans la deuxième boucle pour faire le XOR explicite, tandis que les autres outils de la version SIMD de la même opération.

Cette boucle est appelée à partir des centaines de fois à l'extérieur, afin d'optimiser cette boucle contribuera à réduire le temps de calcul totale.

Le problème est le code SIMD fonctionne plusieurs fois plus lent que le code scalaire.

EDIT: Fait déroulage partiel

__m128i *p1, *p2, *p3, *p4;  
p1 = (__m128i *) simdstore1;  
p2 = (__m128i *) simdstore2;  
p3 = (__m128i *) simdstore3;  
p4 = (__m128i *) simdstore4;  

for (i = 0; i < 20; i++)  
{
    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k = k + 4)  
    {
        simda1  = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);  
        simda2  = _mm_set_epi64x (_mulpre2[u2][k + 1], _mulpre1[u1][k + 1]);  
        simda3  = _mm_set_epi64x (_mulpre2[u2][k + 2], _mulpre1[u1][k + 2]);  
        simda4  = _mm_set_epi64x (_mulpre2[u2][k + 3], _mulpre1[u1][k + 3]);  

        simdb1  = _mm_set_epi64x (res2[i + k], res1[i + k]);  
        simdb2  = _mm_set_epi64x (res2[i + k + 1], res1[i + k + 1]);  
        simdb3  = _mm_set_epi64x (res2[i + k + 2], res1[i + k + 2]);  
        simdb4  = _mm_set_epi64x (res2[i + k + 3], res1[i + k + 3]);  

        simdc1  = _mm_xor_si128 (simda1, simdb1);  
        simdc2  = _mm_xor_si128 (simda2, simdb2);  
        simdc3  = _mm_xor_si128 (simda3, simdb3);  
        simdc4  = _mm_xor_si128 (simda4, simdb4);  

        _mm_store_si128 (p1, simdc1);  
        _mm_store_si128 (p2, simdc2);  
        _mm_store_si128 (p3, simdc3);  
        _mm_store_si128 (p4, simdc4);  

        res1[i + k]= simdstore1[0];  
        res2[i + k]= simdstore1[1]; 
        res1[i + k + 1]= simdstore2[0];  
        res2[i + k + 1]= simdstore2[1];   
        res1[i + k + 2]= simdstore3[0];  
        res2[i + k + 2]= simdstore3[1]; 
        res1[i + k + 3]= simdstore4[0];  
        res2[i + k + 3]= simdstore4[1];   
    }  
}  

Mais, le résultat ne change pas grand-chose; il faut encore deux fois plus longtemps que le code scalaire.

Était-ce utile?

La solution

Disclaimer: Je viens d'un fond PowerPC, donc ce que je dis ici pourrait être complète foutaise. Mais vous caler votre pipeline de vecteur puisque vous essayez d'accéder à vos résultats tout de suite.

Il est préférable de garder tout votre pipeline vectoriel. Dès que vous faites une sorte de conversion de vecteur int ou float, ou stocker le résultat dans la mémoire, vous êtes dilatoire.

Le meilleur mode de fonctionnement en traitant avec SSE ou VMX est: charge, traiter, stocker. Chargez les données dans vos registres vectoriels, faire tout le traitement vectoriel, puis le stocker dans la mémoire.

Je recommande:. Réserve plusieurs registres de __m128i, déroulez votre boucle plusieurs fois, puis enregistrez-le

EDIT:. De plus, si vous déroulez, et si vous RES1 align et RES2 de 16 octets, vous pouvez stocker vos résultats directement dans la mémoire sans passer par cette indirection simdstore, ce qui est probablement un LHS et un autre stand

EDIT: Vous avez oublié l'évidence. Si votre polylen est généralement importante, ne pas oublier de faire une prélecture de cache de données à chaque itération.

Autres conseils

La façon dont votre code ressemble RES1 et RES2 semblent être des vecteurs totalement indépendants. Pourtant, vous les mélanger dans le même registre pour les XOR.

J'utiliser différents registres quelque chose comme ce qui suit (La Les vecteurs doivent tous être alignés).

__m128i x0, x1, x2, x3;  
for (i = 0; i < _polylen; i++)  
{  

    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k+=2)  
    {     
        //res1[i + k] ^= _mulpre1[u1][k];
        x0= _mm_load_si128(&_mulpre1[u1][k]);
        x1= _mm_load_si128(&res1[i + k]);
        x0= _mm_xor_si128 (x0, x1);
        _mm_store_si128 (&res1[i + k], x0);
        //res2[i + k] ^= _mulpre2[u2][k];               
        x2= _mm_load_si128(&_mulpre2[u2][k]);
        x3= _mm_load_si128(&res2[i + k]);
        x2= _mm_xor_si128 (x2, x3);
        _mm_store_si128 (&res2[i + k], x2);
   }     
}  

Notez que j'utilise seulement 4 registres. Vous pouvez manuellement Déroulez utiliser les 8 registres x86 ou plus dans x86_64

Vous faites très peu de calcul ici par rapport au nombre de charges et les magasins qui sont en cours d'exécution, pour un résultat que vous voyez peu d'avantages de SIMD. Vous trouverez sans doute plus utile dans ce cas d'utiliser le code scalaire, en particulier si vous avez un processeur x86-64 que vous pouvez utiliser en mode 64 bits. Cela permettra de réduire le nombre de charges et les magasins, qui sont actuellement le facteur dominant dans votre performance.

(Note:. Vous devriez probablement pas Déroulez la boucle, en particulier si vous utilisez Core 2 ou plus récent)

Je ne suis pas expert SIMD non plus, mais il semble que vous pouvez également bénéficier de préchargement des données, couplé avec mentionné le EboMike derolling. Pourrait également aider si vous avez fusionné RES1 et RES2 dans un réseau aligné (de struct, en fonction de ce que d'autre l'utilise), alors vous n'avez pas besoin de la copie supplémentaire, vous pouvez utiliser directement sur elle.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top