gpt4 book ai didi

c - SIMD 数组添加任意数组长度

转载 作者:太空狗 更新时间:2023-10-29 17:24:29 25 4
gpt4 key购买 nike

我正在通过使用 vector 内在函数重写我的个人图像处理库来学习使用 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 对齐,这似乎不对。

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

最佳答案

一种选择是将数组填充为 16 字节的倍数。然后您可以执行 128 位加载/添加/存储,并在您关心的点之后忽略结果。

对于大型数组,虽然逐字节“结语”的开销将非常小。展开循环可能会更多地提高性能,例如:

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);

关于c - SIMD 数组添加任意数组长度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10167564/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com