【问题标题】:Fast 32-bit array -> 24-bit array conversion in SSE3? (RGB32 -> RGB24)快速 32 位数组 - > SSE3 中的 24 位数组转换? (RGB32 -> RGB24)
【发布时间】:2012-04-16 13:56:58
【问题描述】:

这个问题与之前回答的问题有关: Fast 24-bit array -> 32-bit array conversion? 在一个答案中,interjay 友好地发布了用于转换 RGB24 -> RGB32 的 SSE3 代码,但是我还需要反向转换(RGB32 -> RGB24)。我试了一下(见下文),我的代码确实有效,但它比 interjay 的代码更复杂,而且速度也明显变慢。我看不出如何完全反转指令: _mm_alignr_epi8 在这种情况下似乎没有帮助,但我对 SSE3 并不熟悉。不对称是不可避免的,还是有更快的替代移位和 ORing?

RGB32 -> RGB24:

__m128i *src = ...
__m128i *dst = ...
__m128i mask = _mm_setr_epi8(0,1,2,4, 5,6,8,9, 10,12,13,14, -1,-1,-1,-1);
for (UINT i = 0; i < Pixels; i += 16) {
    __m128i sa = _mm_shuffle_epi8(_mm_load_si128(src), mask);
    __m128i sb = _mm_shuffle_epi8(_mm_load_si128(src + 1), mask);
    __m128i sc = _mm_shuffle_epi8(_mm_load_si128(src + 2), mask);
    __m128i sd = _mm_shuffle_epi8(_mm_load_si128(src + 3), mask);
    _mm_store_si128(dst, _mm_or_si128(sa, _mm_slli_si128(sb, 12)));
    _mm_store_si128(dst + 1, _mm_or_si128(_mm_srli_si128(sb, 4), _mm_slli_si128(sc, 8)));
    _mm_store_si128(dst + 2, _mm_or_si128(_mm_srli_si128(sc, 8), _mm_slli_si128(sd, 4)));
    src += 4;
    dst += 3;
}

RGB24 -> RGB32(由 interjay 提供):

__m128i *src = ...
__m128i *dst = ...
__m128i mask = _mm_setr_epi8(0,1,2,-1, 3,4,5,-1, 6,7,8,-1, 9,10,11,-1);
for (UINT i = 0; i < Pixels; i += 16) {
    __m128i sa = _mm_load_si128(src);
    __m128i sb = _mm_load_si128(src + 1);
    __m128i sc = _mm_load_si128(src + 2);
    __m128i val = _mm_shuffle_epi8(sa, mask);
    _mm_store_si128(dst, val);
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sb, sa, 12), mask);
    _mm_store_si128(dst + 1, val);
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sb, 8), mask);
    _mm_store_si128(dst + 2, val);
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sc, 4), mask);
    _mm_store_si128(dst + 3, val);
    src += 3;
    dst += 4;
}

【问题讨论】:

  • 您只需要在 4 个输入寄存器上使用 6 个掩码即可将它们转换为 3 个输出寄存器。您无法绕过这三个or,因为pshufb 将一个字节设置为零或掩码索引的值。

标签: c bitmap bit-manipulation sse simd


【解决方案1】:

您可以使用this answer 并将随机播放蒙版从 RGB32 更改为 RGB24。

最大的区别是直接计算 shuffle 并使用按位运算来避免移位。另外,使用对齐的流式写入而不是对齐的写入不会污染缓存。

【讨论】:

    【解决方案2】:

    老问题,但我试图解决同样的问题,所以......

    如果您右对齐它的第二个操作数,您可以使用 palignr,即在低字节中放置零。您需要第二个、第三个和第四个单词的左对齐版本,以及第一个、第二个和第三个单词的右对齐版本。

    对于第二个和第三个单词,如果我使用移位从左对齐的版本计算右对齐的版本,GCC 会稍微快乐一些。如果我使用两个不同的 pshufb,它会产生 3 个不必要的动作。

    这里是代码。它正好使用 8 个寄存器;如果您处于 64 位模式,您可以尝试将其展开两个。

        __m128i mask_right = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, 0x80, 0x80, 0x80, 0x80);
        __m128i mask = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0);
    
        for (; n; n -= 16, d += 48, s += 64) {
                __m128i v0 = _mm_load_si128((__m128i *) &s[0]);
                __m128i v1 = _mm_load_si128((__m128i *) &s[16]);
                __m128i v2 = _mm_load_si128((__m128i *) &s[32]);
                __m128i v3 = _mm_load_si128((__m128i *) &s[48]);
    
                v0 = _mm_shuffle_epi8(v0, mask_right);
                v1 = _mm_shuffle_epi8(v1, mask);
                v2 = _mm_shuffle_epi8(v2, mask);
                v3 = _mm_shuffle_epi8(v3, mask);
    
                v0 = _mm_alignr_epi8(v1, v0, 4);
                v1 = _mm_slli_si128(v1, 4);       // mask -> mask_right
                v1 = _mm_alignr_epi8(v2, v1, 8);
                v2 = _mm_slli_si128(v2, 4);       // mask -> mask_right
                v2 = _mm_alignr_epi8(v3, v2, 12);
    
                _mm_store_si128((__m128i *) &d[0], v0);
                _mm_store_si128((__m128i *) &d[16], v1);
                _mm_store_si128((__m128i *) &d[32], v2);
        }
    

    中心部分也可以这样写。编译器少产生一条指令,看起来它有更多的并行性,但需要进行基准测试才能给出正确的答案:

                v0 = _mm_shuffle_epi8(v0, mask_right);
                v1 = _mm_shuffle_epi8(v1, mask);
                v2 = _mm_shuffle_epi8(v2, mask_right);
                v3 = _mm_shuffle_epi8(v3, mask);
    
                __m128i v2l = v2;
                v0 = _mm_alignr_epi8(v1, v0, 4);
                v1 = _mm_slli_si128(v1, 4);             // mask -> mask_right
                v2 = _mm_alignr_epi8(v3, v2, 12);
                v2l = _mm_srli_si128(v2l, 4);           // mask_right -> mask
                v1 = _mm_alignr_epi8(v2l, v1, 8);
    

    【讨论】:

      猜你喜欢
      • 2011-02-27
      • 2020-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-10
      相关资源
      最近更新 更多