2011-02-01 11 views
6

Estoy tratando de normalizar un vector 4d.¿Normalización de SSE más lenta que la aproximación simple?

Mi primera aproximación fue utilizar intrínsecamente SSE, algo que proporcionó un impulso de velocidad 2 veces a mi aritmética vectorial. Aquí es el código básico: (v.v4 es la entrada) (usando GCC) (todo esto se colocarán en línea)

//find squares 
v4sf s = __builtin_ia32_mulps(v.v4, v.v4); 
//set t to square 
v4sf t = s; 
//add the 4 squares together 
s = __builtin_ia32_shufps(s, s, 0x1B); 
t  = __builtin_ia32_addps(t, s); 
s = __builtin_ia32_shufps(s, s, 0x4e); 
t  = __builtin_ia32_addps(t, s); 
s = __builtin_ia32_shufps(s, s, 0x1B); 
t  = __builtin_ia32_addps(t, s); 
//find 1/sqrt of t 
t  = __builtin_ia32_rsqrtps(t); 
//multiply to get normal 
return Vec4(__builtin_ia32_mulps(v.v4, t)); 

puedo comprobar el desmontaje y parece que la forma en que se puede esperar. No veo ningún gran problema allí.

De todas formas, a continuación, lo probé usando una aproximación: (Tengo esto desde Google)

float x = (v.w*v.w) + (v.x*v.x) + (v.y*v.y) + (v.z*v.z); 
float xhalf = 0.5f*x; 
int i = *(int*)&x; // get bits for floating value 
i = 0x5f3759df - (i>>1); // give initial guess y0 
x = *(float*)&i; // convert bits back to float 
x *= 1.5f - xhalf*x*x; // newton step, repeating this step 
// increases accuracy 
//x *= 1.5f - xhalf*x*x; 
return Vec4(v.w*x, v.x*x, v.y*x, v.z*x); 

Se está ejecutando un poco más rápido que la versión SSE! (aproximadamente 5-10% más rápido) Sus resultados también son muy precisos, ¡diría que a 0.001 cuando encuentre longitud! Pero ... GCC me está dando esa regla de alias estricta y coja debido al tipo de juego de palabras.

Así que modifica:

union { 
    float fa; 
    int ia; 
}; 
fa = (v.w*v.w) + (v.x*v.x) + (v.y*v.y) + (v.z*v.z); 
float faHalf = 0.5f*fa; 
ia = 0x5f3759df - (ia>>1); 
fa *= 1.5f - faHalf*fa*fa; 
//fa *= 1.5f - faHalf*fa*fa; 
return Vec4(v.w*fa, v.x*fa, v.y*fa, v.z*fa); 

Y ahora la versión modificada (sin advertencias) va más lento de !! ¡Corre casi el 60% de la velocidad que ejecuta la versión SSE (pero el mismo resultado)! ¿Por qué es esto?

Así que aquí es la pregunta (s):

  1. ¿Es mi implementació SSE correcta?
  2. ¿El SSE es realmente más lento que las operaciones de fpu normales?
  3. ¿Por qué demonios es el 3er código mucho más lento?
+0

Ayudaría saber qué CPU está utilizando. P.ej. antiguas CPUs x86 (pre Core 2) tenían capacidades SSE muy pobres. –

+0

Estoy en un Intel Pentium Dual-Core – Pubby

+3

Duplicado de http://stackoverflow.com/questions/1528727/why-is-sse-scalar-sqrtx-slower-than-rsqrtx-x? – celion

Respuesta

2

Soy un idiota - Me di cuenta de que tenía funcionando SETI @ Home durante la evaluación comparativa. Supongo que estaba matando mi rendimiento SSE. Lo apagué y lo ejecuté dos veces más rápido.

También lo probé en un Athlon de AMD y obtuve los mismos resultados - SSE fue más rápido.

¡Al menos arreglé el error shuf!

0

Supongo que la tercera versión es más lenta porque el compilador decide poner la unión en una variable de memoria. En el caso de conversión, puede copiar los valores de registro a registro. Puedes simplemente mirar el código de máquina generado.

En cuanto a por qué SSE es inexacta, no tengo una respuesta. Ayudaría si puedes dar números reales. Si la diferencia es 0.3 en un vector de tamaño 1, eso sería escandaloso.

+0

El x87 fpu es más preciso porque se calcula internamente utilizando valores de coma flotante de 80 bits. – Trass3r

1

Aquí está el código de ensamblaje más eficiente que se me ocurre. Puede comparar esto con lo que genera su compilador. supongamos que la entrada y la salida están en XMM0.

 ; start with xmm0 = { v.x v.y v.z v.w } 
     movaps %xmm0, %mm1   ; save it till the end 
     mulps %xmm0, %xmm0  ; v=v*v 
     pshufd $1, %xmm0, %xmm1 ; xmm1 = { v.y v.x v.x v.x } 
     addss %xmm0, %xmm1  ; xmm1 = { v.y+v.x v.x v.x v.x } 
     pshufd $3, %xmm0, %xmm2 ; xmm2 = { v.w v.x v.x v.x } 
     movhlps %xmm0, %xmm3  ; xmm3 = { v.z v.w ? ? } 
     addss %xmm1, %xmm3  ; xmm3 = { v.y+v.x+v.z v.x ? ? } 
     addss %xmm3, %xmm2  ; xmm2 = { v.y+v.x+v.z+v.w v.x v.x v.x } 
     rsqrtps %xmm2, %xmm1  ; xmm1 = { rsqrt(v.y+v.x+v.z+v.w) ... } 
     pshufd $0, %xmm1, %xmm1 ; xmm1 = { rsqrt(v.y+v.x+v.z+v.w) x4 } 
     mulps %xmm1, %xmm0  
     ; end with xmm0 = { v.x*sqrt(...) v.y*sqrt(...) v.z*sqrt(...) v.w*sqrt(...) } 
Cuestiones relacionadas