2011-11-04 2 views
1

J'ai commencé à optimiser mon code en utilisant SSE. Essentiellement, il s'agit d'un traceur de rayons qui traite 4 rayons à la fois en stockant les coordonnées dans __m128 types de données x, y, z (les coordonnées pour les quatre rayons sont regroupées par axe). Cependant, j'ai une instruction ramifiée qui protège contre la division par zéro, je ne peux pas sembler convertir en SSE. En série c'est:SSE intrinsèques pour comparaison (_mm_cmpeq_ps) et opération d'affectation

const float d = wZ == -1.0f ? 1.0f/(1.0f-wZ) : 1.0f/(1.0f+wZ); 

Où wZ est coordonnée z et ce calcul doit être fait pour les quatre rayons.

Comment pourrais-je traduire cela en SSE?

J'ai expérimenté en utilisant l'ESS est égale à la comparaison comme suit (WZ maintenant se rapporte à un type de données __m128 contenant les valeurs z pour chacun des quatre rayons):

_mm_cmpeq_ps(_mm_set1_ps(-1.0f) , wZ) 

Et puis en utilisant ceci pour identifier les cas où wZ [x] = -1.0, en prenant la valeur absolue de ce cas, puis continuer le calcul comme d'habitude.

Cependant, je n'ai pas eu beaucoup de succès dans cette entreprise.

+1

Qu'est-ce qui ne va pas avec diviser par zéro? – Pubby

+0

Outre les problèmes évidents, il corrompt les résultats en créant une incohérence à Nz = -1 pour le reste de l'algorithme. – cubiclewar

Répondre

4

Voici une solution assez simple qui implémente simplement le code scalaire avec SSE sans optimisation supplémentaire. Il peut probablement être rendu un peu plus efficace, par ex. en exploitant le fait que le résultat sera de 0.5 quand wZ = -1.0, ou peut-être même en faisant simplement la division indépendamment et en convertissant ensuite le INF en 0.5 après le fait.

J'ai #ifdef d pour SSE4 par rapport à pré-SSE4, puisque SSE4 a une instruction "blend" qui peut être un peu plus efficace que les trois instructions pré-SSE4 qui sont autrement nécessaires pour masquer et sélectionner des valeurs.

#include <emmintrin.h> 
#ifdef __SSE4_1__ 
#include <smmintrin.h> 
#endif 

#include <stdio.h> 

int main(void) 
{ 
    const __m128 vk1 = _mm_set1_ps(1.0f);  // useful constants 
    const __m128 vk0 = _mm_set1_ps(0.0f); 

    __m128 wZ, d, d0, d1, vcmp; 
#ifndef __SSE4_1__ // pre-SSE4 implementation 
    __m128 d0_masked, d1_masked; 
#endif 

    wZ = _mm_set_ps(-1.0f, 0.0f, 1.0f, 2.0f); // test inputs 

    d0 = _mm_add_ps(vk1, wZ);     // d0 = 1.0 - wZ 
    d1 = _mm_sub_ps(vk1, wZ);     // d1 = 1.0 + wZ 
    vcmp = _mm_cmpneq_ps(d1, vk0);    // test for d1 != 0.0, i.e. wZ != -1.0 
#ifdef __SSE4_1__ // SSE4 implementation 
    d = _mm_blendv_ps(d0, d1, vcmp); 
#else    // pre-SSE4 implementation 
    d0_masked = _mm_andnot_ps(vcmp, d0); 
    d1_masked = _mm_and_ps(vcmp, d1); 
    d = _mm_or_ps(d0_masked, d1_masked);  // d = wZ == -1.0 ? 1.0/(1.0 - wZ) : 1.0/(1.0 + wZ) 
#endif 
    d = _mm_div_ps(vk1, d); 

    printf("wZ = %vf\n", wZ); 
    printf("d = %vf\n", d); 

    return 0; 
} 
+0

exactement ce que j'étais après. Il y a quelques opérations que je devrai lire pour bien comprendre le code mais je génère les résultats corrects. Par curiosité, inf ou nan (quoi que 1/0 évalue) peut-il être facilement identifié dans SSE et remplacé? – cubiclewar

+0

Je ne l'ai pas essayé mais je * pense * que vous pouvez exploiter le fait que '_mm_cmpeq_ps (v, v)' retournera false quand 'v' est' INF' ou 'NaN' - je peux essayer une autre solution en utilisant cette méthode plus tard si j'ai le temps ... –

+1

J'ai essayé @ la suggestion de PaulR de filtrer 'INF' /' NaN' en utilisant '_mm_cmpeq_ps (v, v)' comme un masque de bits et cela semble fonctionner correctement. – Rotem

Questions connexes