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

use*_*317 11 c++ sse max 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];
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

Evg*_*uev 12

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的效果以获取原始字节值.

这是一个返回最多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));
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,减去128与添加或与128进行异或相同(因为进位无处可去).`pxor`运行在比'psubb`更多的端口上(并且是可交换的,使优化器在寄存器分配方面具有更大的灵活性),所以你应该更喜欢在范围转换为无符号时. (2认同)

Mar*_*han 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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • @ user46317你是对的,有一个错误.现在它固定了. (2认同)