Я учусь использовать возможности SIMD, пере-написав свою личную библиотеку обработки изображений. с использованием векторных встроенных функций. Одной из основных функций является простой «массив +=
», т.е.
void arrayAdd(unsigned char* A, unsigned char* B, size_t n) {
for(size_t i=0; i < n; i++) { B[i] += A[i] };
}
Для произвольной длины массива очевидный SIMD-код (, предполагающий выравнивание по 16), выглядит примерно так:
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]; }
Но возможно ли ] все дополнения с SIMD-инструкциями? Я думал попробовать:
__m128i mask = (0x100<<8*(n - n16))-1;
_mm_maskmoveu_si128( xmm1, mask, (__m128i*) (B + i) );
для дополнительных элементов, но приведет ли это к неопределенному поведению? mask
должно гарантировать, что доступ фактически не будет осуществлен за пределами массива (, я думаю). Альтернативой является сначала создание дополнительных элементов, но затем массив необходимо выровнять по n-n16
, что кажется неправильным.
Существует ли другой, более оптимальный шаблон таких векторизованных циклов?