Question

La boucle suivante est exécutée sur des centaines de fois.
elma and elmc are both unsigned long (64-bit) arrays, so is res1 and 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++)  
    {     

    1.  //res1[i + k] ^= _mulpre1[u1][k];  
    2.  //res2[i + k] ^= _mulpre2[u2][k];               
    3.        _mm_prefetch ((const void *) &_mulpre2[u2][k], _MM_HINT_T0);
    4.        _mm_prefetch ((const void *) &_mulpre1[u1][k], _MM_HINT_T0);
    5.        simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);
    6.        _mm_prefetch ((const void *) &res2[i + k], _MM_HINT_T0); 
    7.        _mm_prefetch ((const void *) &res1[i + k], _MM_HINT_T0); 
    8.        simdb = _mm_set_epi64x (res2[i + k], res1[i + k]);  
    9.        simdc = _mm_xor_si128 (simda, simdb);  
    10.        _mm_store_si128 (p, simdc);  
    11.        res1[i + k] = simdstore[0];  
    12.        res2[i + k] = simdstore[1];                      
    }     
}  

Dans la boucle, la version scalaire de code (commenté) fonctionne deux fois plus rapide que le code SIMD. Avec sortie cachegrind (instruction lit) des lignes ci-dessus est mentionnée ci-dessous.

Ligne 1: 668.460.000 2 2
Ligne 2: 668460000 1 1
Ligne 3: 89985000 1 1
Ligne 4: 89985000 1 1
Ligne 5: 617.040.000 2 2
Ligne 6: 44992500 0 0
Ligne 7: 44992500 0 0
Ligne 8: 539910000 1 1
Ligne 9: 128550000 0 0
Ligne 10:. . .
Ligne 11: 205680000 0 0
Ligne 12: 205680000 0 0

De la figure ci-dessus, il semble que le commenté (code scalaire) nécessite nombre beaucoup moins d'instructions que le code SIMD.

Comment ce code se fait plus vite?

Était-ce utile?

La solution

Sortez les intrinsics de _mm_prefetch - ils réalisent rien dans ce contexte, et peut même être mal performance. Prélecture ne profite si (a) vous avez une bande passante libre et (b) vous pouvez utiliser la touche de prélecture plusieurs centaines de cycles d'horloge avant lorsque les données sont réellement nécessaire. Je pense que ni (a) ni (b) sont vraies dans votre cas.

Autres conseils

Votre problème de peformance est la suivante:

_mm_set_epi64x (_mulpre2 [u2] [k], _mulpre1 [u1] [k]);

Le mm_set (a, b, c, d) de classe intrinsèques sont très lents. Seuls intrinsics seul ensemble de paramètres (aka diffusion) sont rapides.

Je regarde ce qu'ils font dans le code assembleur.

Ils créent essentiellement un tableau sur la pile, déplacez vos deux entiers des tableaux multidimensionnels ils résident actuellement, à un tableau de pile, utilisant des mouvements de mémoire normale (mov DWORD). Ensuite, à partir de la matrice de pile en utilisant un mouvement de mémoire XMM (mov XMWORD).

La version scalaire passe directement de la mémoire aux registres. PLUS RAPIDE!

Vous voyez les frais généraux provient du fait que le registre XMM ne peut être communiqué avec 128 bits à la fois, de sorte que votre programme est d'abord de commander les 128 bits dans une autre zone de mémoire avant de les charger.

S'il y a un moyen de déplacer 64 valeurs de bits directement ou à partir d'un registre normale à un registre XMM je suis toujours à la recherche pour elle.

Pour obtenir une augmentation de la vitesse de l'utilisation de registres SSE / XMM vos données devront probablement être déjà en ordre dans la mémoire. Chargement de données de commande dans un registre XMM ne vaut que si vous pouvez faire plusieurs opérations XMM par de la charge de la commande. Ici, vous faites une seule opération XOR.

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