如何使用avx2将24位rgb转换为32位?

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

Bee*_*ope 5

使用 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也就是说,如果您从中加载,addraddr + 16应该位于像素边界 ( (addr + 16) % 12 == 0),而不是addr