首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >__m128中最大绝对值的符号,SSE4

__m128中最大绝对值的符号,SSE4
EN

Stack Overflow用户
提问于 2012-11-26 22:36:11
回答 3查看 934关注 0票数 3

我需要知道在__m128中存储有最大绝对值的值的符号。这是我现在的解决方案:

代码语言:javascript
复制
int getMaxSign(__m128 const& vec) {
    static const __m128 SIGN_BIT_MASK = 
      _mm_castsi128_ps(_mm_set1_epi32(0x80000000));

    // This creates an int, where sign(a) is 1 if a is negative, 0 o.w.:
    // sign(a3)<<3 | sign(a2)<<2 | sign(a1)<<1 | sign(a0)
    const int signMask = _mm_movemask_ps(vec);

    // Get the absolute value of the vector;
    __m128 absValsMMX = _mm_andnot_ps(SIGN_BIT_MASK, vec);

    // Figure out the horizontal max
    __declspec(align(16)) float absVals[4];
    _mm_store_ps(absVals, absValsMMX);

    const float maxVal = std::max(std::max(absVals[0], absVals[1]), absVals[2]);

    return (maxVal == absVals[0] ? signMask & 0x1 : 
      (maxVal == absVals[1] ? signMask & 0x2 : signMask & 0x4));
}

在这种情况下,如果具有最大绝对值的值为负,则sign将为1,否则为0,但我实际上并不关心约定是什么。另一件事是,我使用这些__m128s表示同质向量,所以我知道最后一个值始终是0。

对于一个相对简单的任务,这似乎有很多工作要做。我怎样才能更快地做到这一点?

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-11-26 23:56:32

下面是一个可能的实现(用C编写):

代码语言:javascript
复制
int getMaxSign(const __m128 v)
{
    __m128 v1, vmax, vmin, vsign;
    float sign;

    v1 = (__m128)_mm_alignr_epi8((__m128i)v, (__m128i)v, 4); // v1 = v rotated by 1 element
    vmax = _mm_max_ps(v, v1);           // generate horizontal max/min
    vmin = _mm_min_ps(v, v1);
    vmax = _mm_max_ps(vmax, (__m128)_mm_alignr_epi8((__m128i)vmax, (__m128i)vmax, 8));
    vmin = _mm_min_ps(vmin, (__m128)_mm_alignr_epi8((__m128i)vmin, (__m128i)vmin, 8));
    vsign = _mm_add_ps(vmax, vmin);     // add max and min to get sign of abs max
    sign = _mm_extract_ps(vsign, 0);
    return (int)(sign < 0.0f);          // return 1 for negative
}

虽然这看起来像是很多代码,但它只有9条SSE指令,并且没有内存访问,没有分支,标量代码非常少。

请注意,上面同时使用了SSSE3和SSE4.1指令。

下面是第二个版本,它只需要SSSE3:

代码语言:javascript
复制
int getMaxSign(const __m128 v)
{
    __m128 v1, vmax, vmin, vsign;
    int mask;

    v1 = (__m128)_mm_alignr_epi8((__m128i)v, (__m128i)v, 4); // v1 = v rotated by 1 element
    vmax = _mm_max_ps(v, v1);           // generate horizontal max/min
    vmin = _mm_min_ps(v, v1);
    vmax = _mm_max_ps(vmax, (__m128)_mm_alignr_epi8((__m128i)vmax, (__m128i)vmax, 8));
    vmin = _mm_min_ps(vmin, (__m128)_mm_alignr_epi8((__m128i)vmin, (__m128i)vmin, 8));
    vsign = _mm_add_ps(vmax, vmin);     // add max and min to get sign of abs max
    mask = _mm_movemask_epi8((__m128i)vsign);
    return (mask & 8) != 0;             // return 1 for negative
}

这将生成12条指令:

代码语言:javascript
复制
pshufd  $57, %xmm0, %xmm1
movdqa  %xmm0, %xmm2
minps   %xmm1, %xmm2
pshufd  $78, %xmm2, %xmm3
minps   %xmm3, %xmm2
maxps   %xmm1, %xmm0
pshufd  $78, %xmm0, %xmm1
maxps   %xmm1, %xmm0
addps   %xmm2, %xmm0
pmovmskb    %xmm0, %eax
shrl    $3, %eax
andl    $1, %eax

请注意,编译器如何巧妙地将palignr更改为pshufd,并仅使用shrlandl实现最终的标量测试。

Visual Studio C/C++注意:要在__m128__m128i之间进行转换,您需要使用_mm_castps_si128_mm_castsi128_ps,例如

代码语言:javascript
复制
    mask = _mm_movemask_epi8((__m128i)vsign);

需要更改为:

代码语言:javascript
复制
    mask = _mm_movemask_epi8(_mm_castps_si128(vsign));
票数 4
EN

Stack Overflow用户

发布于 2012-11-26 23:03:40

代码语言:javascript
复制
m = min(a,b,c);  
M = max(a,b,c);  

// return abs(m)>abs(M) ? sign(m): sign(M);   // was
return sign(m+M);

正如Paul_R正确注意到的那样,符号简单地来自最小值和最大值的和。以绝对值较大(反符号)的者为胜。

但是这个想法还可以开发得更多: min/max的和等于所有元素的和,减去中间的元素,这可以通过最大3次比较来找到。

代码语言:javascript
复制
return sign(a+b+c - middle(a,b,c));  // or
return sign(a*aw + b*bw + c*cw);     // where aw,bw,cw = [0,1]

aw,bw,cw可以从成功比较的数量中得出(我认为对于这种情况,当有2到3个相等的值时,必须仔细计划)。

以及进一步的:

代码语言:javascript
复制
x = abs(b)>abs(a)?b:a;
return sign(x+c);

甚至可能更远:

代码语言:javascript
复制
s = sign(a + b);  // store the sign of larger of a or b  
a = abs(a); b=abs(b);  
a = max(a,b) | s;   // somehow copy the sign.  
return sign(a+c);  
票数 0
EN

Stack Overflow用户

发布于 2012-11-27 00:40:25

如果您的数字是离散的,并且间隔适当,并且从有限的子集中提取,则还有其他可能性。

例如,如果您保证a、b和c是整数,那么您可以将向量与自身相乘,得到奇数幂,然后用<1,1,1>给它打点。例如,如果我们将它自身乘以4倍,它将得到< a^5,b^5,c^5 >。如果|a|是最大的和|a|=2,那么我们知道b和c将是1或0,所以a^3的值将占主导地位,点积将有它的符号。例如,如果X= < a=-2,b=1,c=0 >,则X^5 = <-32,1,0>。当你用<1,1,1>打点时,你得到-31,它的符号反映了最大绝对值的符号。随着最大数的绝对值的增加,它与其他项之间的差异将趋于收敛-例如,如果我们有<-8,7,7>,那么上面的算法给出了X^5=<-32768,16807,16807>,你用<1,1,1>来打点,得到846,所以算法失败了,指数为5。如果我们把指数增加到7,我们得到<-2097152,823543,823543>,点<1,1,1>给我们-450066,这是正确的答案。最终,舍入错误也会破坏这种方法。但我希望,如果你知道你的数据集的限制,我希望它能为其他替代方案提供一些见解。

作为一个脚注,记住X^5 = (X*X) * (X*X) * X,所以你做一次乘法得到X^2,乘以它本身得到X^4,然后乘以X-三个乘法的总和。你需要一个奇数指数来保持符号。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13567011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档