从 __m128i 中查找最小值/最大值
Posted
技术标签:
【中文标题】从 __m128i 中查找最小值/最大值【英文标题】:Find min/max value from a __m128i 【发布时间】:2016-12-05 23:59:42 【问题描述】:我想使用 SIMD 操作在字节数组中找到最小值/最大值。到目前为止,我能够遍历数组并将最小/最大值存储到 __m128i 变量中,但这意味着我正在寻找的值是混合的(确切地说是其他 15 个)。
我发现这些讨论 here 和 here 用于整数,this page 用于浮点,但我不明白 _mm_shuffle* 是如何工作的。所以我的问题是:
-
我必须执行哪些 SIMD 操作才能从 __m128i 变量中提取最小/最大字节(或无符号字节)值?
_mm_shuffle* 是如何工作的?当我在线查看“最小”文档时,我不明白。我知道它与_MM_SHUFFLE macro 有关,但我不明白这个例子。
【问题讨论】:
如果有帮助,试试software.intel.com/sites/landingpage/IntrinsicsGuide 对于那里记录的大多数内在函数,有详细的伪代码表示它的确切作用 【参考方案1】:这是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
。
如何将其调整为最小值和带符号的最小值/最大值应该是相当明显的。
【讨论】:
非常感谢,我会尽快测试! 您可以使用 PSHUFD (_mm_shuffle_epi32
) 保存一些 MOVDQA 指令,用于最后两次随机播放,因为它们的粒度 >= 4。如果您不需要结果广播,那么您可以去以相反的顺序,将高半部分降低以与低半部分对齐。这将允许使用 PSHUFLW 进行单词洗牌,再次利用它是移动+洗牌的事实。 (PALIGNR 就地更新其目的地,因此如果没有 AVX,编译器必须复制 vmax
,因此它仍然具有原始作为 PMAXUB 的输入)。
@PeterCordes:是的,关于洗牌的好点 - 我假设这不会对性能至关重要,例如仅在最小/最大减少的最后一步需要,但如果是,那么可能值得按照您的建议进行改进。
@PeterCordes:有趣的是,clang 就像你建议的那样使用PSHUFD
s 进行最后两次洗牌:godbolt.org/g/Q7e4U4
不错!查看 clang 的 asm 输出并使用它来改进 C 实现以从其他编译器获得更高效的代码通常是一个好主意。我肯定使用了我没有想到但当场发现的技巧。无论如何,即使不是性能关键,由于 uop-cache / I-cache 的原因,节省代码大小总是可能是一个胜利。每次都使用相同的指令有点好,但水平 - 无论是一种足够常见的模式,一系列不同的整数洗牌不应该让未来的读者感到太困惑。【参考方案2】:
或者,转换为单词并使用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);
您也可以将其用于最大值,但会产生一些开销:补充输入和结果。
【讨论】:
请注意,这需要 SSE 4.1,即 Intel Penryn 或更高版本。可以通过使用 CPUID 并测试 SSE41 位标志在运行时检查支持。以上是关于从 __m128i 中查找最小值/最大值的主要内容,如果未能解决你的问题,请参考以下文章
在 Pandas、Python 中查找具有相同第一列的所有行的最小值、最大值、平均值