Je optimise un certain code pour une micro-architecture Intel X86 Nehalem utilisant SSE intrinsique.
Une partie de mon programme calcule 4 produits DOT et ajoute chaque résultat aux valeurs précédentes dans une pièce contiguë d'une matrice. Plus spécifiquement, P>
tmp0 = _mm_add_ps(tmp0, C_0n); _mm_storeu_ps(C_2, tmp0);
4 Réponses :
Pour le code comme celui-ci, j'aime stocker la "transpose" des A et B, de sorte que {a_0m.x, a_1m.x, a_2m.x, a_3m.x} sont stockés dans un vecteur, etc. Vous pouvez effectuer le produit DOT en utilisant uniquement et ajoute des ajouts, et lorsque vous avez terminé, vous avez tous les 4 produits DOT dans un seul vecteur sans aucun dépassement.
Ceci est utilisé fréquemment dans la rayonnage, pour tester 4 rayons à la fois contre un plan (par exemple, lors de la traversée d'un kd-arbre). Si vous n'avez pas de contrôle sur les données d'entrée, les frais généraux de la transposition ne valent pas la peine. Le code s'exécutera également sur des machines pré-SSE4, bien que cela ne soit pas un problème. P>
une petite note d'efficacité sur le code existant: au lieu de ce p> Il peut être légèrement préférable de faire cela: p> comme les deux premiers espère que cela aide. P> P> mm_add_ps code> S sont complètement indépendants maintenant. En outre, je ne connais pas les horaires relatives d'ajout vs. Shuffling, mais cela pourrait être légèrement plus rapide. P>
Vous pouvez essayer de quitter le résultat du produit DOT dans le mot bas et utilisez le magasin Scalar Store OP Le moyen de le faire est la transposition de Celion. _mm_transpose4_ps macro fera la transposition pour vous . p> _mm_store_ss code> pour enregistrer un flotteur de chaque enregistrement M128 dans l'emplacement approprié de la matrice. Le tampon de magasin de Nehalem devrait accumuler des écrivies consécutives sur la même ligne et les rincer à L1 en lots. p>
Vous devrez encore ajouter l'ancienne valeur (C_0N) à chaque produit DOT avant le magasin. Ils seront tous indépendants, donc ce n'est peut-être pas trop lent, mais ce n'est pas beaucoup plus joli :)
Il est également possible d'utiliser le SSE3 Hadd. Il s'est avéré plus rapidement que d'utiliser _Dot_ps, dans certains tests triviaux. Cela renvoie 4 produits DOT qui pourraient être ajoutés.
static inline __m128 dot_p(const __m128 x, const __m128 y[4]) { __m128 z[4]; z[0] = x * y[0]; z[1] = x * y[1]; z[2] = x * y[2]; z[3] = x * y[3]; z[0] = _mm_hadd_ps(z[0], z[1]); z[2] = _mm_hadd_ps(z[2], z[3]); z[0] = _mm_hadd_ps(z[0], z[2]); return z[0]; }
Je réalise que cette question est ancienne, mais pourquoi utiliser _mm_add_ps code> du tout? Remplacez-le par:
_mm_storeu_ps(C_2, _mm_add_ps(tmp0, C_0));