繁体   English   中英

__m128中至少有4个SP值

[英]Minimum of 4 SP values in __m128

假设有一个__m128变量,其中包含4个SP值,并且您想要最小值,是否有可用的内部函数,或者除了值之间的天真线性比较之外的任何其他函数?

正确知道我的解决方案如下(假设输入__m128变量为x ):

x = _mm_min_ps(x, (__m128)_mm_srli_si128((__m128i)x, 4));
min = _mm_min_ss(x, (__m128)_mm_srli_si128((__m128i)x, 8))[0];

这是非常可怕的,但它正在工作(顺便说一句,有什么像_mm_srli_si128但对于__m128类型?)

没有单一指令/内在但你可以用两个shuffle和两个分钟来做:

__m128 _mm_hmin_ps(__m128 v)
{
    v = _mm_min_ps(v, _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 1, 0, 3)));
    v = _mm_min_ps(v, _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 0, 3, 2)));
    return v;
}

输出向量将包含输入向量中所有元素的min,在整个输出向量中复制。

Paul R的答案很棒! (@Paul R - 如果你读过这个谢谢你!)我只想试着解释它对于像我这样的SSE新手的实际效果。 当然我可能在某处错了,所以欢迎任何更正!

_mm_shuffle_ps如何工作?

首先,SSE寄存器的索引与您的预期相反,如下所示:

[6, 9, 8, 5] // values
 3  2  1  0  // indexes

这种索引顺序使向量左移将数据从低索引移动到高索引,就像将整数位左移一样。 最重要的元素在左边。


_mm_shuffle_ps可以混合两个寄存器的内容:

// __m128 a : (a3, a2, a1, a0)
// __m128 b : (b3, b2, b1, b0)
__m128 two_from_a_and_two_from_b = _mm_shuffle_ps(b, a, _MM_SHUFFLE(3, 2,   1, 0));
//                                                                  ^  ^    ^  ^ 
//                                            indexes into second operand    indexes into first operand
// two_from_a_and_two_from_b : (a3, a2, b1, b0)

在这里,我们只想改变一个寄存器的值,而不是两个。 我们可以通过将v作为两个参数传递来做到这一点(你可以在Paul R的函数中看到这个):

// __m128 v : (v3, v2, v1, v0)
__m128 v_rotated_left_by_1 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 1, 0, 3));
// v_rotated_left_by_1 : (v2, v1, v0, v3) // i.e. move all elements left by 1 with wraparound

我打算将它包装在宏中以便于阅读:

#define mm_shuffle_one(v, pattern)  _mm_shuffle_ps(v, v, pattern)

(它不能是函数,因为_mm_shuffle_pspattern参数在编译时必须是常量。)

这是实际函数的略微修改版本 - 我为可读性添加了中间名称,因为编译器无论如何都会优化它们:

inline __m128 _mm_hmin_ps(__m128 v){
    __m128  v_rotated_left_by_1 = mm_shuffle_one(v,  _MM_SHUFFLE(2, 1, 0, 3));
    __m128 v2 = _mm_min_ps(v,   v_rotated_left_by_1);

    __m128 v2_rotated_left_by_2 = mm_shuffle_one(v2, _MM_SHUFFLE(1, 0, 3, 2));
    __m128 v3 = _mm_min_ps(v2, v2_rotated_left_by_2);

    return v3;
}

为什么按照我们的方式改变元素? 我们如何通过仅两min操作找到最小的四个元素?

我只好跟着你如何一些麻烦min 4辆花车只有两个矢量min的操作,但我的理解是,当我手动跟着它的值是min “D一起,一步一步来。 (虽然自己做比阅读它更有趣)

说我们有v

[7,6,9,5] v

首先,我们minvv_rotated_left_by_1

[7,6,9,5] v
 3 2 1 0  // (just the indices of the elements)
[6,9,5,7] v_rotated_left_by_1
 2 1 0 3  // (the indexes refer to v, and we rotated it left by 1, so the indices are shifted)
--------- min
[6,6,5,5] v2
 3 2 1 0 // (explained
 2 1 0 3 //  below    )

v2元素下的每一列跟踪v哪些索引min以获得该元素 因此,从左到右依次列:

v2[3] == 6 == min(v[3], v[2])
v2[2] == 6 == min(v[2], v[1])
v2[1] == 5 == min(v[1], v[0])
v2[0] == 5 == min(v[0], v[3])

现在第二min

[6,6,5,5] v2
 3 2 1 0
 2 1 0 3
[5,5,6,6] v2_rotated_left_by_2
 1 0 3 2
 0 3 2 1
--------- min
[5,5,5,5] v3
 3 2 1 0
 2 1 0 3
 1 0 3 2
 0 3 2 1

瞧! v3下的每一列都包含(3,2,1,0) - v3每个元素都是min ,所有元素都是v - 所以每个元素都包含整个向量v的最小值。

使用该函数后,您可以使用float _mm_cvtss_f32(__m128)提取最小值:

__m128 min_vector = _mm_hmin_ps(my_vector);
float minval = _mm_cvtss_f32(min_vector);

***

这只是一个切向思想,但我发现有趣的是这种方法可以扩展到任意长度的序列,将前一步骤的结果旋转1, 2, 4, 8, ... 2**ceil(log2(len(v))) (我认为)在每一步。 从理论角度来看这很酷 - 如果你可以同时按元素比较两个序列,你可以在对数时间内找到序列的最小值/最大值1

1这延伸到所有水平折叠/缩减,如总和。 相同的洗牌,不同的垂直操作。

但是,AVX(256位向量)使128位边界变得特殊,并且更难以随机播放。 如果你只想要一个标量结果,提取高半部分,这样每一步都可以将矢量宽度缩小一半。 (就像以最快的方式在x86上进行水平浮点矢量和 ,对于128位向量,它具有比2x shufps更高效的shuffle,在没有AVX的情况下编译时避免了一些movaps指令。)

但是如果你想把结果广播到像@PaulR的答案这样的每一个元素,你想要进行在线改组(即在每个通道中的4个元素内旋转),然后交换一半,或者旋转128位通道。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM