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
.