2012-01-13 16 views
9

que tienen una gran pieza de código, parte de cuyo cuerpo contiene este pedazo de código:SSE más lento que FPU?

result = (nx * m_Lx + ny * m_Ly + m_Lz)/sqrt(nx * nx + ny * ny + 1); 

que he vectorizado de la siguiente manera (todo lo que ya es un float):

__m128 r = _mm_mul_ps(_mm_set_ps(ny, nx, ny, nx), 
         _mm_set_ps(ny, nx, m_Ly, m_Lx)); 
__declspec(align(16)) int asInt[4] = { 
    _mm_extract_ps(r,0), _mm_extract_ps(r,1), 
    _mm_extract_ps(r,2), _mm_extract_ps(r,3) 
}; 
float (&res)[4] = reinterpret_cast<float (&)[4]>(asInt); 
result = (res[0] + res[1] + m_Lz)/sqrt(res[2] + res[3] + 1); 

El resultado es correcto; sin embargo, mi evaluación comparativa muestra que la versión vectorizada es lento:

  • La versión no vectorizado toma 3750 ms
  • La versión vectorizada lleva a 4050 ms
  • Configuración result-0 directamente (y la eliminación de esta parte del código completo) reduce todo el proceso a 2500 ms

Dado que la versión vectorizada sólo contiene uno conjunto de multiplicaciones de SSE (en lugar de cuatro multiplicaciones de FPU individuales), ¿por qué es más lento? ¿Es efectivamente la FPU más rápida que la SSE, o hay una variable de confusión aquí?

(estoy en un Core i5 móvil.)

+7

Hace un tiempo que no veía una pregunta de SSE en SO. Supongo que todos volverán de las vacaciones.:) – Mysticial

+0

@Mysticial: LOL. xD – Mehrdad

Respuesta

15

vas a pasar mucho tiempo moviendo los valores escalares a/desde SSE se registra con _mm_set_ps y _mm_extract_ps - esto está generando una gran cantidad de instrucciones, el tiempo de ejecución de los cuales superará con creces cualquier beneficio de usar _mm_mul_ps. Eche un vistazo a la salida del conjunto generado para ver cuánto código se está generando además de la instrucción simple MULPS.

Para vectorizar esto correctamente, necesita usar cargas y almacenes SSE de 128 bits (_mm_load_ps/_mm_store_ps) y luego usar las instrucciones de mezcla SSE para mover los elementos dentro de los registros donde sea necesario.

Un punto más a tener en cuenta: las CPU modernas como Core i5, Core i7, tienen dos FPU escalares y pueden emitir 2 multiplicaciones de punto flotante por reloj. El beneficio potencial de SSE para el punto flotante de precisión simple es por lo tanto solo 2x en el mejor de los casos. Es fácil perder la mayor parte/todo este beneficio 2x si tiene demasiadas instrucciones de "limpieza", como es el caso aquí.

+0

¡Ah, no me di cuenta de que mover valores entre registros era muy lento! En realidad estaba tratando de * evitar * las operaciones de memoria. Genial saber, muchas gracias! :) +1 – Mehrdad

+3

@Mehrdad Es lento porque se mueve entre registros en diferentes dominios (SSE-FP vs. registros generales). Generalmente hay una penalización extra de 1-2 ciclos para el movimiento de datos entre dominios. – Mysticial

+0

@Mysticial: Ooooo buen punto - Olvidé totalmente que el registro FPU es bastante diferente de un registro de propósito general. :) – Mehrdad

0

Mi opinión sería que el procesador tiene el tiempo para calcular la primera multiplicación cuando usa la FPU mientras carga los siguientes valores. El SSE tiene que cargar todos los valores primero.

3

Hay varios problemas:

  1. No verá mucho beneficios del uso de las instrucciones SSE en este tipo de operaciones, debido a que las instrucciones SSE se supone que es mejor en operaciones paralelas (es decir, la multiplicación de varios valores en el Mismo tiempo). Lo que hizo es un uso indebido de SSE
  2. No establezca los valores, utilice el puntero al primer valor de la matriz, pero sus valores no están en la matriz
  3. No extraiga ni copie valores en la matriz . Eso también es un mal uso de SSE. El resultado se supone que está en una matriz.
Cuestiones relacionadas