Estoy aprendiendo a usar las capacidades de SIMD volviendo a escribir mi biblioteca de procesamiento de imagen personal utilizando vectores intrínsecos. Una función básica es una simple "array +=
", es decirAgregación de matriz SIMD para longitudes de matriz arbitrarias
void arrayAdd(unsigned char* A, unsigned char* B, size_t n) {
for(size_t i=0; i < n; i++) { B[i] += A[i] };
}
Para longitudes de matriz arbitrarias, el código SIMD obvio (suponiendo alineado por 16) es algo como:
size_t i = 0;
__m128i xmm0, xmm1;
n16 = n - (n % 16);
for (; i < n16; i+=16) {
xmm0 = _mm_load_si128((__m128i*) (A + i));
xmm1 = _mm_load_si128((__m128i*) (B + i));
xmm1 = _mm_add_epi8(xmm0, xmm1);
_mm_store_si128((__m128i*) (B + i), xmm1);
}
for (; i < n; i++) { B[i] += A[i]; }
Pero es posible hacer todos las adiciones con instrucciones SIMD? Pensé en probar esto:
__m128i mask = (0x100<<8*(n - n16))-1;
_mm_maskmoveu_si128(xmm1, mask, (__m128i*) (B + i));
para los elementos extra, pero ¿dará lugar a un comportamiento indefinido? El mask
debería garantizar que no se realiza ningún acceso más allá de los límites de la matriz (creo). La alternativa es hacer los elementos adicionales primero, pero luego la matriz debe estar alineada por n-n16
, lo que no parece correcto.
¿Hay algún otro patrón más óptimo como bucles vectorizados?
se podría asegurar que en el código de las longitudes de matriz no son siempre múltiplos de 16 bytes (aunque posiblemente menos elementos se utilizan en realidad), por lo que este epílogo no aparece. Pero el epílogo realmente no es importante en términos de velocidad. – Walter