2011-02-15 21 views
11

Estoy tratando de encontrar una forma eficiente de cargar los flotantes de tiempo constante de compilación en los registros SSE (2/3). He intentado hacer un código simple como este,Cargar flotantes constantes en los registros SSE

const __m128 x = { 1.0f, 2.0f, 3.0f, 4.0f }; 

pero eso genera 4 instrucciones movss de la memoria!

movss  xmm0,dword ptr [[email protected] (14048E534h)] 
movss  xmm1,dword ptr [[email protected] (14048E530h)] 
movaps  xmm6,xmm12 
shufps  xmm6,xmm12,0C6h 
movss  dword ptr [rsp],xmm0 
movss  xmm0,dword ptr [[email protected] (14048E52Ch)] 
movss  dword ptr [rsp+4],xmm1 
movss  xmm1,dword ptr [[email protected] (14048E528h)] 

que cargan los escalares dentro y fuera de la memoria ... (?!?!)

Al hacer esto, aunque ..

float Align(16) myfloat4[4] = { 1.0f, 2.0f, 3.0f, 4.0f, }; // out in global scope 

genera.

movaps  xmm5,xmmword ptr [::myarray4 (140512050h)] 

Idealmente, sería bueno si tengo constantes de sus sería una manera que ni siquiera la memoria táctil y simplemente lo hacen con las instrucciones de estilo inmediatos (por ejemplo, las constantes compilados en la propia instrucción).

Gracias

constantes
+2

Para código SSE/2 de alto rendimiento, recomiendo usar GCC/ICC. Lee esto para obtener más información sobre por qué: http://www.liranuna.com/sse-intrinsics-optimizations-in-popular-compilers/ – LiraNuna

Respuesta

6

Si desea forzarlo a una sola carga, puede intentar (gcc):

__attribute__((aligned(16))) float vec[4] = { 1.0f, 1.1f, 1.2f, 1.3f }; 
__m128 v = _mm_load_ps(vec); // edit by sor: removed the "&" cause its already an address 

Si tiene Visual C++, use __declspec(align(16)) para solicitar la restricción adecuada.

En mi sistema, esto (compilado con gcc -m32 -msse -O2; sin optimización en todos los desordena el código, pero todavía conserva la única movaps al final) crea el siguiente código ensamblador (gcc/AT & sintaxis T):

andl $-16, %esp 
    subl $16, %esp 
    movl $0x3f800000, (%esp) 
    movl $0x3f8ccccd, 4(%esp) 
    movl $0x3f99999a, 8(%esp) 
    movl $0x3fa66666, 12(%esp) 
    movaps (%esp), %xmm0 

Tenga en cuenta que alinea el stackpointer antes de asignar stackspace y poner las constantes allí. Dejando fuera el __attribute__((aligned)) puede, dependiendo de su compilador, crear código incorrecto que no lo haga, así que tenga cuidado y verifique el desensamblaje.

Además:
Puesto que usted ha estado pidiendo cómo poner constantes en el código, simplemente tratan los anteriores, con un partido de clasificación para la matriz staticfloat.Eso crea el siguiente montaje:

movaps vec.7330, %xmm0 
    ... 
vec.7330: 
    .long 1065353216 
    .long 1066192077 
    .long 1067030938 
    .long 1067869798 
2

Normalmente tales como esto se cargan antes de cualquier bucles o partes "calientes" del código, por lo que el rendimiento no debería ser tan importante. Pero si no puede evitar hacer este tipo de cosas dentro de un bucle, primero probaría _mm_set_ps y vería qué genera eso. Pruebe también ICC en lugar de gcc, ya que tiende a generar un mejor código.

+0

Estoy usando Visual Studio y _mm_set_ps está generando más movss. Creo que el compilador de Visual Studio es simplemente terrible. – coderdave

+2

@coderdave: sí Visual Studio genera un código SSE bastante malo; también es un dolor usar para SSE ya que tiene toda clase de estúpidas restricciones ABI y otras molestias; use gcc o mejor aún ICC si puede –

3

Primero, ¿en qué nivel de optimización está compilando? No es raro ver ese tipo de codegen en -O0 o -O1, pero estaría bastante sorprendido de verlo con -O2 o superior en la mayoría de los compiladores.

En segundo lugar, no hay cargas inmediatas en SSE. Se puede hacer una carga inmediata a un GPR, a continuación, pasar ese valor al SSE, pero no se puede evocar otros valores sin una carga real (haciendo caso omiso de ciertos valores especiales como 0 o (int)-1, que se puede producir a través de las operaciones lógicas.

último , si el código incorrecto se genera con las optimizaciones activadas y en una ubicación crítica para el rendimiento, envíe un error al compilador.

+0

Estoy compilando sin ninguna duda -02 por lo que parece que la generación de código de Visual Studio es mala. A medida que investigo más, parece que este es el consenso y la mayoría de las personas no usan VC para SSE y simplemente usan ensamblador u otro compilador – coderdave

+0

@coderdave: Por favor, presente un error contra VS, entonces. La única forma en que los Estados miembros sabrán que deben dedicar recursos al problema es si las personas se quejan de ello. –

+0

Mientras SSE no hace cargas inmediatas (excepto para trucos como 'pxor' para ceros o' pcmpeq' para unos) carga de la memoria, '_mm_load_ps()', así que no hay nada de malo en crear una matriz en la pila y cargando el registro SSE desde allí. –

2

Generación de constantes es mucho más simple (y más rápido) si las cuatro constantes de flotación son los mismos. Por ejemplo, el patrón de bits para 1.f es 0x3f800000. Una manera en que se pueden generar utilizando SSE2

 register __m128i onef; 
     __asm__ ("pcmpeqb %0, %0" : "=x" (onef)); 
     onef = _mm_slli_epi32(onef, 25); 
     onef = _mm_srli_epi32(onef, 2); 

Otro enfoque con SSE4.1 es,

 register uint32_t t = 0x3f800000; 
     register __m128 onef; 
     __asm__ ("pinsrd %0, %1, 0" : "=x" (onef) : "r" (t)); 
     onef = _mm_shuffle_epi32(onef, 0); 

en cuenta que yo no soy de possitive si esta versión es más rápido que el SSE2 uno, no tiene perfilado, solo probado el resultado fue correcto.

Si los valores de cada uno de los cuatro flotantes deben ser diferentes, cada una de las constantes se puede generar y mezclar o mezclar.

Ya sea que esto sea útil o no, esto depende de si es probable que falle la caché, de lo contrario, cargar la constante de la memoria es más rápido. Los trucos como este son muy útiles en vmx/altivec, pero las cachés grandes en la mayoría de las PC pueden hacer que esto sea menos útil para sse.

Hay una buena discusión de esto en el Manual de optimización de Agner Fog, libro 2, sección 13.4, http://www.agner.org/optimize/.

Nota final, el uso del ensamblador en línea anterior es específico de gcc, el motivo es permitir el uso de variables sin inicializar sin generar una advertencia del compilador. Con vc, puede o no necesitar inicializar primero las variables con _mm_setzero_ps(), luego esperar que el optimizador pueda eliminar esto.

Cuestiones relacionadas