2012-04-16 53 views
6

我正在学习使用向量内在函数重写我的个人图像处理库来使用SIMD功能。一个基本功能是一个简单的“阵列+=”,即为任意数组长度添加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,这看起来不正确。

是否有另一种更优化的模式,如矢量化循环?

+0

你可以确保你的代码长度数组总是的16个字节的倍数(实际上,虽然使用可能更少的元素),所以这绝不收尾出现。但是,在速度方面,epilog并不重要。 – Walter 2012-04-16 15:34:38

回答

4

一种选择是将您的阵列填充到16个字节的倍数。然后,你可以做128位加载/添加/存储,并简单地忽略你关心的点后的结果。

对于大型数组,尽管逐字节“epilog”的开销将非常小。展开循环可能会进一步提高性能,如下所示:

for (; i < n32; i+=32) { 
    xmm0 = _mm_load_si128((__m128i*) (A + i)); 
    xmm1 = _mm_load_si128((__m128i*) (B + i)); 
    xmm2 = _mm_load_si128((__m128i*) (A + i + 16)); 
    xmm3 = _mm_load_si128((__m128i*) (B + i + 16)); 
    xmm1 = _mm_add_epi8(xmm0, xmm1); 
    xmm3 = _mm_add_epi8(xmm2, xmm3); 
    _mm_store_si128((__m128i*) (B + i), xmm1); 
    _mm_store_si128((__m128i*) (B + i + 16), xmm3); 
} 
// Do another 128 bit load/add/store here if required 

但是,如果不进行某些分析就很难说。

你也可以在最后做一个未对齐的加载/存储(假设你有超过16个字节),尽管这可能不会有很大的不同。例如。如果你有20个字节你一个加载/存储,以抵消0和另一个未对齐加载/添加/存储(_mm_storeu_si128__mm_loadu_si128),以抵消4.

你可以使用_mm_maskmoveu_si128,但你需要让面膜到XMM寄存器,你的示例代码不起作用。您可能希望将掩码寄存器设置为全部FF,然后使用移位对齐它。在一天结束时,它可能会比未对齐的加载/添加/存储慢。

这将是这样的:

mask = _mm_cmpeq_epi8(mask, mask); // Set to all FF's 
mask = _mm_srli_si128(mask, 16-(n%16)); // Align mask 
_mm_maskmoveu_si128(xmm, mask, A + i); 
+0

在实践中,我会将掩码放在查找表中。你认为它会比“epilog”循环慢吗? – 2012-04-16 03:05:00

+0

@reve_etrange:可能不会更慢,但如果不测量这两种解决方案,很难知道。试一试。 – 2012-04-16 03:43:43

+0

我会给它一个镜头。但是它是合法的内存访问吗?由于* mask的* some *值可能导致数组边界违例。 – 2012-04-16 03:46:47