使用SSE的水平最小值和最大值

时间:2014-03-07 17:17:49

标签: c++ max sse minimum avx

我有一个使用SSE的功能来做很多事情,并且探查器向我显示我用来计算水平最小值和最大值的代码部分大部分时间消耗。

我一直在使用以下实现,例如:

static inline int16_t hMin(__m128i buffer) {
    buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m1));
    buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m2));
    buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m3));
    buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m4));
    return ((int8_t*) ((void *) &buffer))[0];
}

如您所见,我需要计算16个1字节整数的最小值和最大值。

非常感谢任何好的建议:)

由于

2 个答案:

答案 0 :(得分:11)

SSE 4.1的指令几乎可以满足你的要求。它的名字是PHMINPOSUW,C / C ++内在是_mm_minpos_epu16。它仅限于16位无符号值,不能给出最大值,但这些问题很容易解决。

  1. 如果您需要找到最少的非负字节,则不执行任何操作。如果字节可能是负数,则为每个字节添加128。如果您需要最大值,请从127减去每个。
  2. 使用_mm_srli_pi16_mm_shuffle_epi8,然后使用_mm_min_epu8获取偶数字节中的8个成对最小值和某些XMM寄存器的奇数字节中的零。 (这些零是由shift / shuffle指令产生的,应该在_mm_min_epu8之后保留在它们的位置。
  3. 使用_mm_minpos_epu16查找这些值中的最小值。
  4. 使用_mm_cvtsi128_si32提取最终的最小值。
  5. 撤消步骤1的效果以获取原始字节值。
  6. 这是一个返回最多16个有符号字节的示例:

    static inline int16_t hMax(__m128i buffer)
    {
        __m128i tmp1 = _mm_sub_epi8(_mm_set1_epi8(127), buffer);
        __m128i tmp2 = _mm_min_epu8(tmp1, _mm_srli_epi16(tmp1, 8));
        __m128i tmp3 = _mm_minpos_epu16(tmp2);
        return (int8_t)(127 - _mm_cvtsi128_si32(tmp3));
    }
    

答案 1 :(得分:8)

我建议进行两项修改:

  • ((int8_t*) ((void *) &buffer))[0]替换为_mm_cvtsi128_si32
  • _mm_shuffle_epi8替换为_mm_shuffle_epi32 / _mm_shufflelo_epi16,它们在最近的AMD处理器和Intel Atom上具有较低的延迟,并且可以节省内存加载操作:

    static inline int16_t hMin(__m128i buffer)
    {
        buffer = _mm_min_epi8(buffer, _mm_shuffle_epi32(buffer, _MM_SHUFFLE(3, 2, 3, 2)));
        buffer = _mm_min_epi8(buffer, _mm_shuffle_epi32(buffer, _MM_SHUFFLE(1, 1, 1, 1)));
        buffer = _mm_min_epi8(buffer, _mm_shufflelo_epi16(buffer, _MM_SHUFFLE(1, 1, 1, 1)));
        buffer = _mm_min_epi8(buffer, _mm_srli_epi16(buffer, 8));
        return (int8_t)_mm_cvtsi128_si32(buffer);
    }