从__M128i中查找最小值/最大值

Find min/max value from a __m128i

本文关键字:最大值 最小值 M128i 查找      更新时间:2023-10-16

我想使用SIMD操作找到最小/最大值到字节数组。到目前为止,我能够浏览数组并将最小值/最大值存储到__M128i变量中,但这意味着我正在寻找的值是混合的(准确地说是15个)。

我在这里和这里找到了这些讨论,用于整数,此页面for float,但我不了解works _mm_shuffle*。所以我的问题是:

  1. 我必须执行什么SIMD操作才能从__M128i变量中提取最小/最大字节(或无符号字节)值?
  2. _mm_shuffle*如何工作?当我在线查看"最小"文档时,我不会明白它。我知道这与_mm_shuffle宏有关,但我不举个例子。

这是uint8_t的水平最大值的示例:

#include "tmmintrin.h" // requires SSSE3
__m128i _mm_hmax_epu8(const __m128i v)
{
    __m128i vmax = v;
    vmax = _mm_max_epu8(vmax, _mm_alignr_epi8(vmax, vmax, 1));
    vmax = _mm_max_epu8(vmax, _mm_alignr_epi8(vmax, vmax, 2));
    vmax = _mm_max_epu8(vmax, _mm_alignr_epi8(vmax, vmax, 4));
    vmax = _mm_max_epu8(vmax, _mm_alignr_epi8(vmax, vmax, 8));
    return vmax;
}

最大值将以所有元素返回。如果您需要作为标量的值,请使用_mm_extract_epi8

应该很明显地如何适应它,并签署的最小/最大

另外,转换为单词并使用 phminposuw(未测试)

int hminu8(__m128i x)
{
  __m128i l = _mm_unpacklo_epi8(x, _mm_setzero_si128());
  __m128i h = _mm_unpackhi_epi8(x, _mm_setzero_si128());
  l = _mm_minpos_epu16(l);
  h = _mm_minpos_epu16(h);
  return _mm_extract_epi16(_mm_min_epu16(l, h), 0);
}

从我的快速计数中,延迟比一个分钟/随机级联的级别差一些,但是吞吐量要好一些。phminposuw的链接答案可能会更好。适用于未签名的字节(但未测试)

uint8_t hminu8(__m128i x)
{
  x = _mm_min_epu8(x, _mm_srli_epi16(x, 8));
  x = _mm_minpos_epu16(x);
  return _mm_cvtsi128_si32(x);
}

您也可以将其用于Max,但是有一点开销:补充输入和结果。