Wik*_*ang 2 rgb x86 sse simd avx2
我已经用 SSSE3 完成了这个,现在我想知道这是否可以用 AVX2 完成以获得更好的性能?
我用一个零字节填充 24 位 rgb,使用来自Fast 24-bit array -> 32-bit array conversion 的代码?.
static const __m128i mask = _mm_setr_epi8(0, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8, -1, 9, 10, 11, -1);
for (size_t row = 0; row < height; ++row)
{
for (size_t column = 0; column < width; column += 16)
{
const __m128i *src = reinterpret_cast<const __m128i *>(in + row * in_pitch + column + (column << 1));
__m128i *dst = reinterpret_cast<__m128i *>(out + row * out_pitch + (column << 2));
__m128i v[4];
v[0] = _mm_load_si128(src);
v[1] = _mm_load_si128(src + 1);
v[2] = _mm_load_si128(src + 2);
v[3] = _mm_shuffle_epi8(v[0], mask);
_mm_store_si128(dst, v[3]);
v[3] = _mm_shuffle_epi8(_mm_alignr_epi8(v[1], v[0], 12), mask);
_mm_store_si128(dst + 1, v[3]);
v[3] = _mm_shuffle_epi8(_mm_alignr_epi8(v[2], v[1], 8), mask);
_mm_store_si128(dst + 2, v[3]);
v[3] = _mm_shuffle_epi8(_mm_alignr_epi8(v[2], v[2], 4), mask);
_mm_store_si128(dst + 3, v[3]);
}
}
Run Code Online (Sandbox Code Playgroud)
问题是 _mm256_shuffle_epi8 分别洗牌高 128 位和低 128 位,所以掩码不能只是替换为
_mm256_setr_epi8(0, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8, -1, 9, 10, 11, -1, 12, 13, 14, -1, 15, 16, 17, -1, 18, 19, 20, -1, 21, 22, 23, -1);
Run Code Online (Sandbox Code Playgroud)
也_mm_alignr_epi8需要替换为_mm256_permute2x128_si256和_mm256_alignr_epi8
使用 AVX2,您可以一次有效地处理 8 个像素(24 个输入字节和 32 个输出字节)。
你只需要调整你的负载,让你将处理像素的24字节的块为中心的32个字节的负载的中间,而不是负载对准的像素块的开始通常的做法2。这意味着车道边界将落在像素 4 和 5 之间,并且您将在每个车道中拥有恰好 4 个像素的字节。结合适当的shuffle mask,这应该是SSE的两倍。
例如:
给定一个输入指针,uint8_t input[]您使用非 SIMD 代码1处理前四个像素,然后执行第一个 32 字节加载,input[8]以便低位通道(字节 0-15)获得像素 4、5、6 的 12 个有效负载字节,7 在其高位字节中,紧接着是接下来的 12 个有效载荷字节,用于高通道中接下来的 4 个像素。然后您使用pshufb将像素扩展到它们的正确位置(您需要为每条车道使用不同的蒙版,因为您将低车道中的像素移向较低位置,而将高车道中的像素移至较高位置,但这不会提出问题)。然后下一次加载将在input[26](24 个字节之后),依此类推。
对于完美缓存的输入/输出,您应该使用这种方法获得每周期约 8 个像素的吞吐量 - 限制为 1/cycle 存储吞吐量和 1/cycle shuffle 吞吐量。幸运的是,这种方法与始终对齐的存储兼容(因为存储增量是 32 字节)。您将有一些未对齐的负载,但这些负载仍可能以 1/周期发生,因此不应成为瓶颈。
值得注意的是,就 SIMD 指令集扩展而言,这种类型的方法“仅适用一次”:当您有 2 个通道时它可以工作,但不能更多(因此相同的想法不适用于具有 4 128 个通道的 512 位 AVX512 -位通道)。
1这避免了在输入数组之前读取越界:如果您知道这是安全的,则可以避免这一步。
2也就是说,如果您从中加载,addr则addr + 16应该位于像素边界 ( (addr + 16) % 12 == 0),而不是addr。
| 归档时间: |
|
| 查看次数: |
525 次 |
| 最近记录: |