2012-04-19 10 views
8

Para un proyecto de hobby en el que estoy trabajando, necesito emular ciertas operaciones enteras de 64 bits en una CPU x86, y debe ser rápido.Emulación SSE optimizada de enteros de 64 bits

Actualmente, estoy haciendo esto a través de instrucciones MMX, pero eso es realmente un dolor de trabajar, porque tengo que lavar el estado de registro fp todo el tiempo (y porque la mayoría de instrucciones MMX se ocupan de firmado enteros, y Necesito un comportamiento sin firmar).

Así que me pregunto si los gurús de SSE/optimización aquí en SO pueden llegar a una mejor implementación utilizando SSE.

Las operaciones que necesito son las siguientes (bastante específicos):

uint64_t X, Y; 

X = 0; 
X = 1; 
X << 1; 
X != Y; 
X + 1; 
X & 0x1 // get lsb 
X | 0x1 // set lsb 
X > Y; 

Específicamente, no necesito Además de propósito general o el paso de, por ejemplo, basta con añadir una e izquierda de desplazamiento de uno. Realmente, solo las operaciones exactas se muestran aquí.

Excepto, por supuesto, en x86, uint64_t se emula utilizando dos escalares de 32 bits, que es lento (y, en mi caso, simplemente no funciona, porque necesito que las cargas/tiendas sean atómicas, lo que no lo serán al cargar/almacenar dos registros separados).

Por lo tanto, necesito una solución SIMD. Algunas de estas operaciones son triviales, ya soportadas por SSE2. Otros (!= y <) requieren un poco más de trabajo.

Sugerencias? SSE y SSE2 están bien.Haría falta un poco de persuasión para permitir SSE3 y SSE4 es, probablemente, fuera de la cuestión (una CPU que soporta SSE4 es probable que se ejecute de 64 bits todos modos, por lo que no necesito estas soluciones)

+0

La adición de entero de 64 bits se admite directamente en SSE2. Supongo que también necesita multiplicaciones de 64 bits. 64 x 64 -> 64 bits (mitad inferior), o ¿necesita 64 x 64 -> 128 bits? – Mysticial

+0

No es necesario multiplicar, solo las operaciones específicas que mostré arriba (por lo tanto, ni siquiera general, simplemente incremente en 1. Y sí, además de SSE2, pero pensé que también podría mostrar todas las operaciones que necesitaba, para completar. Solo significa que algunos de ellos son fáciles :) – jalf

+0

¿A qué quieres que lleguen los operadores lógicos? Registro general? o SSE? – Mysticial

Respuesta

14

SSE2 ha apoyo directo para algunas operaciones de enteros de 64 bits:

Conjunto ambos elementos a 0:

__m128i z = _mm_setzero_si128(); 

Establecer ambos elementos a 1:

__m128i z = _mm_set_epi32(0,1,0,1); 

verticalmente añadir/restar cada entero de 64 bits:

__m128i z = _mm_add_epi64(x,y) 
__m128i z = _mm_sub_epi64(x,y) 

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_arithmetic.htm#intref_sse2_integer_arithmetic

Shift Izquierda:

__m128i z = _mm_slli_epi64(x,i) // i must be an immediate 

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_int_shift.htm

operadores bit a bit:

__m128i z = _mm_and_si128(x,y) 
__m128i z = _mm_or_si128(x,y) 

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_logical.htm

SSE no tiene incrementos, por lo que tendrá que usar una constante con 1.


Las comparaciones son más difíciles ya que no hay soporte de 64 bits.

Aquí es el de la igualdad:

__m128i t = _mm_cmpeq_epi32(a,b); 
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177)); 

Esto establecerá el elemento de cada uno de 64 bits para 0xffffffffffff si son iguales. Si lo quiere como 0 o 1 en un int, puede sacarlo usando _mm_cvtsi32_si128() y agregue 1.

Y menos-que: (no completamente probado)

a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000)); 
b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000)); 
__m128i t = _mm_cmplt_epi32(a,b); 
__m128i u = _mm_cmpgt_epi32(a,b); 
__m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177)); 
z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z); 

Esto establecerá el elemento cada 64 bits para 0xffffffffffff si el elemento correspondiente en a es menor que b.


Aquí hay versiones de "iguales" y "menores que" que devuelven un bool. Devuelven el resultado de la comparación para el entero de 64 bits inferior.

inline bool equals(__m128i a,__m128i b){ 
    __m128i t = _mm_cmpeq_epi32(a,b); 
    __m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177)); 
    return _mm_cvtsi128_si32(z) & 1; 
} 
inline bool lessthan(__m128i a,__m128i b){ 
    a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000)); 
    b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000)); 
    __m128i t = _mm_cmplt_epi32(a,b); 
    __m128i u = _mm_cmpgt_epi32(a,b); 
    __m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177)); 
    z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z); 
    return _mm_cvtsi128_si32(z) & 1; 
} 
+0

Creo que mi caso de "menos que" es incorrecto. Voy a volver a verificar ... – Mysticial

+0

Acabo de actualizar el código "menor que". Sin embargo, no estoy 100% seguro de si es correcto. – Mysticial

+0

Escribí una prueba rápida del caso "menor que" [aquí] (http://ideone.com/U4LXd) para VS2010. Podría necesitar un poco de ajuste para ejecutar en otros compiladores. Falla para '0x00000000 <0x00000001' (a menos que haya cometido un error en la prueba) – jalf

Cuestiones relacionadas