【问题标题】:De-interleave image channel in SSE 16 bit vectorsSSE 16 位向量中的去交错图像通道
【发布时间】:2016-06-24 04:35:28
【问题描述】:

字节我有 32 bpp 图像。我需要在不同的 16 位向量中去交错 R G B 颜色通道,我正在使用以下代码来执行此操作(how to deinterleave image channel in SSE

  // deinterleave chaneel R, G, B ,A in 16 bits vectors
  {
     __m128i vrgba = _mm_loadu_si128((__m128i *)(pSrc));
     __m128i vr1 = _mm_and_si128(vrgba, _mm_set1_epi32(0xff));
     __m128i vg1 = _mm_and_si128(_mm_srli_epi32(vrgba, 8), _mm_set1_epi32(0xff));
     __m128i vb1 = _mm_and_si128(_mm_srli_epi32(vrgba, 16), _mm_set1_epi32(0xff));
     __m128i va1 = _mm_srli_epi32(vrgba, 24);

     vrgba = _mm_loadu_si128((__m128i *)(pSrc + 4));  // since pSrc is uint32_t type
     __m128i vr2 = _mm_and_si128(vrgba, _mm_set1_epi32(0xff));
     __m128i vg2 = _mm_and_si128(_mm_srli_epi32(vrgba, 8), _mm_set1_epi32(0xff));
     __m128i vb2 = _mm_and_si128(_mm_srli_epi32(vrgba, 16), _mm_set1_epi32(0xff));
     __m128i va2 = _mm_srli_epi32(vrgba, 24);

     vr = _mm_packs_epi32(vr1, vr2);
     vg = _mm_packs_epi32(vg1, vg2);
     vb = _mm_packs_epi32(vb1, vb2);
     va = _mm_packs_epi32(va1, va2);
  }

我们可以提高效率吗?下面是没有去交织通道的高斯代码。我发现它非常低效

    static inline void ConvertTo16Bits(__m128i& v1, __m128i& v2, const __m128i& v0)
    {
        __m128i const zero = _mm_setzero_si128();
        v1 = _mm_unpacklo_epi8(v0, zero);
        v2 = _mm_unpackhi_epi8(v0, zero);
    }

    static inline void mul32bits(__m128i &vh, __m128i &vl,           // output - 2x4xint32_t
        const __m128i& v0, const __m128i& v1) // input  - 2x8xint16_t
    {
        const __m128i vhi = _mm_mulhi_epu16(v0, v1);
        const __m128i vlo = _mm_mullo_epi16(v0, v1);
        vh = _mm_unpacklo_epi16(vlo, vhi);
        vl = _mm_unpackhi_epi16(vlo, vhi);
    }

    struct Pixel
    {
        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char a;
    };

    void computePixelvalue(unsigned int * pixelArray, int count, unsigned short * gaussArray, Pixel& out)
    {
        __m128i sumRGBA;
        sumRGBA = _mm_set1_epi32(0);
        unsigned int countMod4 = count % 4;
        unsigned int b, g, r, a;
        constexpr int shuffle = _MM_SHUFFLE(3, 1, 0, 0);

        while (count >= 4)
        {
            __m128i vrgba = _mm_loadu_si128((__m128i *)(pixelArray));
            __m128i rgba12, rgba34;

            ConvertTo16Bits(rgba12, rgba34, vrgba);

            unsigned short s1 = *gaussArray++;
            unsigned short s2 = *gaussArray++;

            __m128i shift8 = _mm_set1_epi16(s1);
            __m128i shift16 = _mm_set1_epi16(s2);
            __m128i gaussVector = _mm_shuffle_epi32(_mm_unpacklo_epi32(shift8, shift16), shuffle);

            __m128i multl, multh;
            mul32bits(multl, multh, rgba12, gaussVector);
            sumRGBA = _mm_add_epi32(sumRGBA, multl);
            sumRGBA = _mm_add_epi32(sumRGBA, multh);

            s1 = *gaussArray++;
            s2 = *gaussArray++;
            shift8 = _mm_set1_epi16(s1);
            shift16 = _mm_set1_epi16(s2);
            gaussVector = _mm_shuffle_epi32(_mm_unpacklo_epi32(shift8, shift16), shuffle);

            mul32bits(multl, multh, rgba34, gaussVector);
            sumRGBA = _mm_add_epi32(sumRGBA, multl);
            sumRGBA = _mm_add_epi32(sumRGBA, multh);

            count = count - 4;
            pixelArray = pixelArray + 4;
        }

        r = sumRGBA.m128i_u32[0];
        g = sumRGBA.m128i_u32[1];
        b = sumRGBA.m128i_u32[2];
        a = sumRGBA.m128i_u32[3];

        while (countMod4)
        {
            auto pixelArrayByte = reinterpret_cast<unsigned char*>(pixelArray);

            unsigned short k = static_cast<unsigned short>(*gaussArray++);
            r += *pixelArrayByte++ * k;
            g += *pixelArrayByte++ * k;
            b += *pixelArrayByte++ * k;
            a += *pixelArrayByte++ * k;

            countMod4--;
        }

        out.r = static_cast<unsigned char>(r >> 15);
        out.g = static_cast<unsigned char>(g >> 15);
        out.b = static_cast<unsigned char>(b >> 15);
        out.a = static_cast<unsigned char>(a >> 15);
    }

【问题讨论】:

  • 如果可以的话,首先以平面格式存储数据。
  • 从您之前的问题来看,您似乎只想应用高斯过滤器,因此实际上不需要像这样将 RGBA 组件分开。您可以将过滤器应用于原始格式的数据,不是吗?
  • 分离使 accumptlation 变得容易,不是吗.. 一旦我去交错,我可以像这样使用 Rsum = Rsum + Rvector(16bit* gaussVector 16 bit) 最后我可以把它放在目标位置为 Rsum[0] + Rsum[1]+ rSum[2]。显然,另一种方法是首先以平面格式存储您的数据,但我想由于内存有限,我可以在这里买一份副本
  • 无论您是否去交错,累积都大致相同 - 去交错数据的唯一轻微优势是当您在 X 轴上应用过滤器时水平偏移更小。性能方面,我怀疑您最好只使用交错数据,代码也应该更简单。
  • 实际上我最初是这样做的,但我的去交错代码明显优于交错代码。你想看看吗?

标签: x86 sse simd intrinsics sse2


【解决方案1】:

pshufb{ a b g r ... } 的向量转换为{ a a a a b b b b g g g g r r r r } 的向量(每个源向量一个 pshufb)。

punpckldq 在两个混洗的源向量之间得到{ g2g2g2g2 g1g1g1g1 r2r2r2r2 r1r1r1r1 }pmovzxbw 低半部分,高半部分用零解包,得到只有 g 和 r 的向量。

同理,punpckhdq同样的两个源向量得到{ a2a2a2a2 a1a1a1a1 b2b2b2b2 b1b1b1b1 }

所以每 4 个输入向量(产生 8 个输出向量),即:

  • 4x pshufb(全部使用相同的控制掩码)
  • 2x punpckh/l dq
  • 4x punpckh/l bw(或将其中 2 个替换为 pmovzxbw)

总共 10 条 ALU 指令,不包括任何复制以避免破坏仍然需要的数据。

这与 mask/shift/pack 方法所需的总共 32 条指令相比相当不错。 (如果没有 AVX,这将涉及相当多的复制来以 4 种不同的方式屏蔽相同的向量。)其中 8 条指令是 pack shuffle 指令,因此对 shuffle 端口的压力要小一些,以换取更多总指令。

Haswell 只能在一个执行端口上洗牌,这与位移位不同。 (并且_mm_and 可以在三个向量执行端口中的任何一个上运行)。我非常有信心 10 次洗牌方式会以相当大的优势获胜,因为有更多的计算可以与之重叠。


shufps 作为来自两个源向量的随机播放可能很有用,但它具有 32 位粒度,所以我看不出它有什么用处。在 Intel SnB 系列和 AMD Bulldozer 系列上,在整数向量指令之间使用它不会受到惩罚。


另一个想法:

__m128i rgba1 = _mm_loadu_si128((__m128i *)(pSrc));   // { a1.4 b1.4 g1.4 r1.4 ... a1.1 b1.1 g1.1 r1.1 }
__m128i rgba2 = _mm_loadu_si128((__m128i *)(pSrc+4)); // { a2.4 b2.4 ... g2.1 r2.1 }

 __m128i rg1 = _mm_and_si128 (rgba1, _mm_set1_epi32(0xffff));
 __m128i rg2 = _mm_slli_epi32(rgba2, 16);

 __m128i rg_interleaved = _mm_or_si128(rg2, rg1);    // { g2.4 r2.4  g1.4 r1.4 ... g2.1 r2.1  g1.1 r1.1 }

用另一个_mm_and_si128 和一个_mm_srli_epi16rg_interleaved 分成零扩展的16 位r 和g 向量。

【讨论】:

  • 谢谢彼得,我有一些想法,但我对此很天真,所以如果您能提供方法 1 的代码,将会很有帮助
  • @BharatAhuja:不,谢谢,为_mm_shuffle_epi8 计算出正确的_mm_set_epi8 常量并不是很有趣。为未来读者的利益,在实施和测试后继续发布您自己的答案。此外,如果 Paul R 说您应该能够在不以这种方式解包的情况下实现高斯滤波器,那么它可能是真的。你应该调查一下。英特尔已经发布了一些示例,例如software.intel.com/en-us/search/site/field_tags/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-04
  • 2012-05-27
  • 1970-01-01
  • 2021-11-08
  • 2016-06-23
  • 2020-05-07
相关资源
最近更新 更多