从 __m128i 中查找 min/max 值
Find min/max value from a __m128i
我想使用 SIMD 操作将 minimum/maximum 值放入字节数组中。到目前为止,我能够遍历数组并将 minimum/maximum 值存储到 __m128i 变量中,但这意味着我正在寻找的值与其他值混合在一起(确切地说是另外 15 个)。
我发现了这些关于浮动的讨论 here and here for integer, and this page,但我不明白它是如何工作的 _mm_shuffle*。所以我的问题是:
- 我必须执行哪些 SIMD 操作才能从 __m128i 变量中提取最小/最大字节(或无符号字节)值?
- _mm_shuffle* 是如何工作的?当我在线查看 "minimal" 文档时,我不明白。我知道它与 _MM_SHUFFLE macro 有关,但我不明白这个例子。
这是 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
.
对于 min 和 signed min/max.
如何调整它应该是相当明显的
或者,转换为单词并使用 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);
}
根据我的快速计算,延迟比 min/shuffle 级联差一点,但吞吐量好一点。 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,但需要一些开销:补充输入和结果。
我想使用 SIMD 操作将 minimum/maximum 值放入字节数组中。到目前为止,我能够遍历数组并将 minimum/maximum 值存储到 __m128i 变量中,但这意味着我正在寻找的值与其他值混合在一起(确切地说是另外 15 个)。
我发现了这些关于浮动的讨论 here and here for integer, and this page,但我不明白它是如何工作的 _mm_shuffle*。所以我的问题是:
- 我必须执行哪些 SIMD 操作才能从 __m128i 变量中提取最小/最大字节(或无符号字节)值?
- _mm_shuffle* 是如何工作的?当我在线查看 "minimal" 文档时,我不明白。我知道它与 _MM_SHUFFLE macro 有关,但我不明白这个例子。
这是 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
.
对于 min 和 signed min/max.
如何调整它应该是相当明显的或者,转换为单词并使用 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);
}
根据我的快速计算,延迟比 min/shuffle 级联差一点,但吞吐量好一点。 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,但需要一些开销:补充输入和结果。