Pregunta

Estoy tratando de averiguar la mejor manera de pre-calcular el pecado y el coseno de valores, almacenarlos en bloques alineados y, a continuación, utilizar posteriormente para ESS cálculos:

En el inicio de mi programa, me cree un objeto con el miembro:

static __m128 *m_sincos;

entonces me inicializar ese miembro en el constructor:

m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
for (int t=0; t<Bins; t++)
  m_sincos[t] = _mm_set_ps(cos(t), sin(t), sin(t), cos(t));



Cuando me vaya a utilizar m_sincos, me encuentro con tres problemas:
-Los datos no parecen estar alineados

movaps xmm0, m_sincos[t] //crashes
movups xmm0, m_sincos[t] //does not crash

-Las variables no parecen ser correctos

movaps result, xmm0 // returns values that are not what is in m_sincos[t]
//Although, putting a watch on m_sincos[t] displays the correct values

-Lo que realmente me confunde es que esto hace que todo funcione (pero es demasiado lento):

__m128 _sincos = m_sincos[t];
movaps xmm0, _sincos
movaps result, xmm0
¿Fue útil?

Solución

m_sincos[t] es una expresión C En una instrucción de ensamblaje, sin embargo, (__asm?), Se interpreta como un modo de direccionamiento X86, con un resultado completamente diferente. Por ejemplo, VS2008 SP1 compila:

movaps xmm0, m_sincos[t]

En: (vea la ventana de desmontaje cuando la aplicación se bloquee en modo de depuración)

movaps xmm0, xmmword ptr [t]

Que la interpretación intenta copiar un valor de 128 bits almacenado en la dirección de la variable t en xmm0. t, sin embargo, es un valor de 32 bits en una probable dirección no alineada. Es probable que la ejecución de la instrucción cause una falla de alineación y le daría resultados incorrectos en el caso impar en el que tLa dirección está alineada.

Puede solucionar esto utilizando un modo de direccionamiento X86 apropiado. Aquí está la versión lenta pero clara:

__asm mov eax, m_sincos                  ; eax <- m_sincos
__asm mov ebx, dword ptr t
__asm shl ebx, 4                         ; ebx <- t * 16 ; each array element is 16-bytes (128 bit) long
__asm movaps xmm0, xmmword ptr [eax+ebx] ; xmm0 <- m_sincos[t]

Nota al margen:

Cuando pongo esto en un programa completo, ocurre algo extraño:

#include <math.h>
#include <tchar.h>
#include <xmmintrin.h>

int main()
{
    static __m128 *m_sincos;
    int Bins = 4;

    m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
    for (int t=0; t<Bins; t++) {
        m_sincos[t] = _mm_set_ps(cos((float) t), sin((float) t), sin((float) t), cos((float) t));
        __asm movaps xmm0, m_sincos[t];
        __asm mov eax, m_sincos
        __asm mov ebx, t
        __asm shl ebx, 4
        __asm movaps xmm0, [eax+ebx];
    }

    return 0;
}

Cuando ejecuta esto, si vigila la ventana de los registros, puede notar algo extraño. Aunque los resultados son correctos, xmm0 está obteniendo el valor correcto antes de la movaps Se ejecuta la instrucción. ¿Cómo sucede eso?

Una mirada al código de ensamblaje generado muestra que _mm_set_ps() Cargue los resultados de sin/cos en xmm0, luego lo guarda a la dirección de memoria de m_sincos[t]. Pero el valor permanece allí en xmm0 también. _mm_set_ps es una "intrínseca", no una llamada de función; No intenta restaurar los valores de los registros que usa después de que se haga.

Si hay una lección a tomar de esto, podría ser que al usar las funciones intrínsecas SSE, úselas en todo momento, para que el compilador pueda optimizar las cosas por usted. De lo contrario, si está utilizando el ensamblaje en línea, úselo en todo momento.

Otros consejos

Usted siempre debe usar la instrinsics o simplemente encender y salir de ellos, en vez de forma explícita la codificación en.Esto es debido a que __asm no es portable a 64 bits de código.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top