如何使用 SSE 将 _m128i 转换为无符号整数?

Posted

技术标签:

【中文标题】如何使用 SSE 将 _m128i 转换为无符号整数?【英文标题】:How do I convert _m128i to an unsigned int with SSE? 【发布时间】:2011-12-22 02:50:28 【问题描述】:

我已经制作了一个图像分色功能。

// =(
#define ARGB_COLOR(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))

inline UINT PosterizeColor(const UINT &color, const float &nColors)

    __m128 clr = _mm_cvtepi32_ps(  _mm_cvtepu8_epi32((__m128i&)color)  );

    clr = _mm_mul_ps(clr,  _mm_set_ps1(nColors / 255.0f)  );
    clr = _mm_round_ps(clr, _MM_FROUND_TO_NEAREST_INT);
    clr = _mm_mul_ps(clr, _mm_set_ps1(255.0f / nColors)  );

    __m128i iClr = _mm_cvttps_epi32(clr);

    return ARGB_COLOR(iClr.m128i_u8[12],
                      iClr.m128i_u8[8],
                      iClr.m128i_u8[4],
                      iClr.m128i_u8[0]);

在第一行中,我将颜色解压缩为 4 个浮点数,但找不到正确的反向操作方法。

我搜索了 SSE 文档,找不到 _mm_cvtepu8_epi32 的反面

存在吗?

【问题讨论】:

【参考方案1】:

您需要_mm_shuffle_epi8_mm_cvtsi128_si32 的组合:

static const __m128i shuffleMask = _mm_setr_epi8(0,  4,  8, 12, -1, -1, -1, -1,
                                               -1, -1, -1, -1, -1, -1, -1, -1);
UINT color = _mm_cvtsi128_si32(_mm_shuffle_epi8(iClr, shuffleMask));

【讨论】:

小错误:_mm_set_epi8 应该是_mm_setr_epi8。否则,这确实有效!我很惊讶,+1。我没有意识到 SSSE3 有字节粒度洗牌。 谢谢你,但我也发现我可以做到这一点,这给了我与上面几乎相同的结果: iClr = _mm_packs_epi32(iClr, _mm_setzero_si128()); iClr = _mm_packus_epi16(iClr, _mm_setzero_si128()); UINT 颜色 = (UINT)_mm_extract_epi32(iClr, 0); -- 我还使用整数数学重写了例程,然后将其转换为 asm,这对于 2048x2048 图像产生了 8~9 ms 的最佳结果。【参考方案2】:

不幸的是,即使在 AVX 中也没有执行此操作的说明(我不知道)。所以你必须像现在一样手动完成。

但是,您当前的方法非常不理想,您依赖于 .m128i_u8,它是一个 MSVC 扩展。根据我对 MSVC 的经验,它将使用对齐的缓冲区来访问各个元素。由于部分字访问,这会带来非常严重的惩罚。

使用_mm_extract_epi32() 代替.m128i_u8。这是在 SSE4.1 中。但是您已经通过 _mm_cvtepu8_epi32() 依赖 SSE4.1。

这种情况特别糟糕,因为您使用的是 1 字节粒度。如果您使用的是 2 字节(16 位整数)粒度,则使用shuffle intrinsics 是一种有效的解决方案。

【讨论】:

_mm_extract_epi32() 有点帮助,但是“非常次优”是什么意思? 一般来说,当你存储一些数据,然后立即尝试以不同的字长再次访问它时,会产生巨大的惩罚。据我所见,MSVC 提取xmm 寄存器组件的方法正是这样做的:存储到内存中,并分别访问单词。但我想这个特殊情况并不是最坏的情况。 好吧,无论如何,对于 2048x2048,它的运行时间为 22 毫秒,我认为现在已经足够了。谢谢 =) @Mysticial:在最近的 µarch 中,这些摊位已大大减少。请参阅英特尔优化手册,了解哪些案例可以直接转发而不会导致停顿。 这个答案对关于不可能洗牌的说法具有误导性。

以上是关于如何使用 SSE 将 _m128i 转换为无符号整数?的主要内容,如果未能解决你的问题,请参考以下文章

SSE:将 __m128 转换为浮点数

SSE:如何将 _m128i._i32[4] 减少到 _m128i._i8

SSE:从 const __m128 * 转换为 const float *

使用 AVX/AVX2/SSE __m128i 将所有负数字节设置为 -128 (0x80) 并保留所有其他字节

__m128, SSE4 中最大绝对值的符号

如何从 16 x 8 位 __m128i 值中提取 32 x 4 位整数