使用 SSE 在 __m128i 向量中获取最小做空值?
Getting min short value in a __m128i vector with SSE?
这个问题似乎与 Getting max value in a __m128i vector with SSE? 相似,但使用短裤和最小值而不是整数 + 最大值。这是我想出的:
typedef short int weight;
weight horizontal_min_Vec4i(__m128i x) {
__m128i max1 = _mm_shufflehi_epi16(x, _MM_SHUFFLE(0, 0, 3, 2));
__m128i max1b = _mm_shufflelo_epi16(x, _MM_SHUFFLE(0, 0, 3, 2));
__m128i max2 = _mm_min_epi16(max1, max1b);
//max2 = _mm_min_epi16(max2, x);
max1 = _mm_shufflehi_epi16(max2, _MM_SHUFFLE(0, 0, 0, 1));
max1b = _mm_shufflelo_epi16(max2, _MM_SHUFFLE(0, 0, 0, 1));
__m128i max3 = _mm_min_epi16(max1, max1b);
max2 = _mm_min_epi16(max2, max3);
return min(_mm_extract_epi16(max2, 0), _mm_extract_epi16(max2, 4));
}
对于x的上下两部分,函数与中的答案基本相同。所以,我知道最小值在 __m128i 变量 max2 的位置 0 或 4。虽然它比下面显示的无 SIMD 函数 horizontal_min_Vec4i_Plain(__m128i x)
快得多,但恐怕瓶颈是最后一行的 _mm_extract_epi16 operation
。有没有更好的方法来实现这一点,以获得更好的加速?我正在使用 Haswell,所以我可以访问最新的 SSE 扩展。
weight horizontal_min_Vec4i_Plain(__m128i x) {
weight result[8] __attribute__((aligned(16)));
_mm_store_si128((__m128i *) result, x);
weight myMin = result[0];
for (int l = 1; l < 8; l++) {
if (myMin > result[l]) {
myMin = result[l];
}
}
return myMin;
}
有符号比较和无符号比较几乎相同,只是设置了最高位的范围在无符号比较中被视为比没有设置最高位的范围更大,而在有符号比较中被视为更小。这意味着有符号和无符号比较可以通过这些规则相互转换:
x <s y = (x ^ signbit) <u (y ^ signbit)
x <u y = (x ^ signbit) <s (y ^ signbit)
这个属性直接转min
和max
,所以:
min_s(x, y) = min_u(x ^ signbit, y ^ signbit) ^ signbit
然后我们可以使用_mm_minpos_epu16
来处理水平最小值,总共得到类似
的东西
__m128i xs = _mm_xor_si128(x, _mm_set1_epi16(0x8000));
return _mm_extract_epi16(_mm_minpos_epu16(xs), 0) - 0x8000;
- 0x8000
是 ^ 0x8000
和符号扩展(extract
零扩展)合二为一。
这个问题似乎与 Getting max value in a __m128i vector with SSE? 相似,但使用短裤和最小值而不是整数 + 最大值。这是我想出的:
typedef short int weight;
weight horizontal_min_Vec4i(__m128i x) {
__m128i max1 = _mm_shufflehi_epi16(x, _MM_SHUFFLE(0, 0, 3, 2));
__m128i max1b = _mm_shufflelo_epi16(x, _MM_SHUFFLE(0, 0, 3, 2));
__m128i max2 = _mm_min_epi16(max1, max1b);
//max2 = _mm_min_epi16(max2, x);
max1 = _mm_shufflehi_epi16(max2, _MM_SHUFFLE(0, 0, 0, 1));
max1b = _mm_shufflelo_epi16(max2, _MM_SHUFFLE(0, 0, 0, 1));
__m128i max3 = _mm_min_epi16(max1, max1b);
max2 = _mm_min_epi16(max2, max3);
return min(_mm_extract_epi16(max2, 0), _mm_extract_epi16(max2, 4));
}
对于x的上下两部分,函数与中的答案基本相同。所以,我知道最小值在 __m128i 变量 max2 的位置 0 或 4。虽然它比下面显示的无 SIMD 函数 horizontal_min_Vec4i_Plain(__m128i x)
快得多,但恐怕瓶颈是最后一行的 _mm_extract_epi16 operation
。有没有更好的方法来实现这一点,以获得更好的加速?我正在使用 Haswell,所以我可以访问最新的 SSE 扩展。
weight horizontal_min_Vec4i_Plain(__m128i x) {
weight result[8] __attribute__((aligned(16)));
_mm_store_si128((__m128i *) result, x);
weight myMin = result[0];
for (int l = 1; l < 8; l++) {
if (myMin > result[l]) {
myMin = result[l];
}
}
return myMin;
}
有符号比较和无符号比较几乎相同,只是设置了最高位的范围在无符号比较中被视为比没有设置最高位的范围更大,而在有符号比较中被视为更小。这意味着有符号和无符号比较可以通过这些规则相互转换:
x <s y = (x ^ signbit) <u (y ^ signbit)
x <u y = (x ^ signbit) <s (y ^ signbit)
这个属性直接转min
和max
,所以:
min_s(x, y) = min_u(x ^ signbit, y ^ signbit) ^ signbit
然后我们可以使用_mm_minpos_epu16
来处理水平最小值,总共得到类似
__m128i xs = _mm_xor_si128(x, _mm_set1_epi16(0x8000));
return _mm_extract_epi16(_mm_minpos_epu16(xs), 0) - 0x8000;
- 0x8000
是 ^ 0x8000
和符号扩展(extract
零扩展)合二为一。