c++ x86 sse simd intrinsics

c++ - ¿Cómo realizar eficientemente la conversión int8/int64 con SSE?



x86 simd (2)

Estoy implementando conversiones entre tipos de SSE y descubrí que la implementación de la conversión int8-> int64 ampliando para los objetivos anteriores a SSE4.1 es engorrosa.

La implementación directa sería:

inline __m128i convert_i8_i64(__m128i a) { #ifdef __SSE4_1__ return _mm_cvtepi8_epi64(a); #else a = _mm_unpacklo_epi8(a, a); a = _mm_unpacklo_epi16(a, a); a = _mm_unpacklo_epi32(a, a); return _mm_srai_epi64(a, 56); // missing instrinsic! #endif }

Pero como _mm_srai_epi64 no existe hasta que AVX-512, hay dos opciones en este punto:

  • implementando _mm_srai_epi64 , o
  • implementando convert_i8_i64 de una manera diferente.

No estoy seguro de cuál sería la solución más eficiente. ¿Alguna idea?


Los intrínsecos de desempaquetado se usan aquí de una manera divertida. "Duplican" los datos, en lugar de agregar la extensión de los signos, como era de esperar. Por ejemplo, antes de la primera iteración que tiene en su registro, la siguiente

x x x x x x x x x x x x x x a b

Si convierte b en 16 bits, debe obtener esto:

x x x x x x x x x x x x A a B b

Aquí A y B son extensiones de signo de b , es decir, ambas son 0 o -1.

En lugar de esto, tu código da

x x x x x x x x x x x x a a b b

Y luego lo convierte al resultado correcto al cambiar a la derecha.

Sin embargo, no está obligado a utilizar el mismo operando dos veces en los intrínsecos "desempaquetar". Puede obtener el resultado deseado si "desempaquetó" los siguientes dos registros:

x x x x x x x x x x x x x x a b x x x x x x x x x x x x x x A B

Es decir:

a = _mm_unpacklo_epi8(a, _mm_srai_epi8(a, 8));

(si ese _mm_srai_epi8 intrínseco realmente existió)

Puede aplicar la misma idea a la última etapa de su conversión. Desea "descomprimir" los siguientes dos registros:

x x x x x x x x A A A a B B B b x x x x x x x x A A A A B B B B

Para obtenerlos, desplaza a la derecha los datos de 32 bits:

_mm_srai_epi32(a, 24) _mm_srai_epi32(a, 32)

Entonces, el último "desempaquetar" es

_mm_unpacklo_epi32(_mm_srai_epi32(a, 24), _mm_srai_epi32(a, 32));


Con SSSE3, puede usar pshufb para evitar la mayoría de los desempaquetados. Usando la notación a / A Anatoly:

;; input in xmm0 ;; x x x x x x x x | x x x x x x a b pshufb xmm0, [low_to_upper] ;; a 0 0 0 0 0 0 0 | b 0 0 0 0 0 0 0 psrad xmm0, 24 ;; A A A a 0 0 0 0 | B B B b 0 0 0 0 pshufb xmm0, [bcast_signextend]; A A A A A A A a | B B B B B B B b

Sin SSSE3, creo que es posible que pueda hacer algo con PSHUFLW, PSHUFD y tal vez POR en lugar de algunos de los pasos de PUNPCK. Pero nada de lo que he pensado es en realidad mejor que los desempaquetados, a menos que esté en una CPU Core2 u otra de ralentización lenta, donde pshuflw es más rápido que punpcklbw .