3

__m128に格納されている最大絶対値を持つ値の符号を知る必要があります。これは私が今持っている解決策です:

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));
}

この場合、最大絶対値の値が負の場合は符号が1になり、それ以外の場合は0になりますが、実際には規則が何であるかは気にしません。もう1つは、これらの__m128を使用して同種のベクトルを表現しているため、最後の値が常に0になることを知っています。

これは、比較的単純なタスクのために行うべき多くの作業のようです。どうすればこれをより速く行うことができますか?

ありがとう!

4

3 に答える 3

4

これが(Cでの)1つの可能な実装です:

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
}

これは多くのコードのように見えますが、SSE命令は約9つであり、メモリアクセス、分岐、およびスカラーコードはほとんどありません。

上記では、SSSE3とSSE4.1の両方の命令が使用されていることに注意してください。

SSSE3のみを必要とする2番目のバージョンは次のとおりです。

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個の命令が生成されます。

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し、aと。pshufdだけを使用して最終的なスカラーテストを実装する方法に注意してください。shrlandl


Visual Studio C / C ++に関する注意:間でキャストするには、とを使用する必要が__m128あり__m128iます。例:_mm_castps_si128_mm_castsi128_ps

    mask = _mm_movemask_epi8((__m128i)vsign);

次のように変更する必要があります。

    mask = _mm_movemask_epi8(_mm_castps_si128(vsign));
于 2012-11-26T15:56:31.943 に答える
0
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が正しく認識しているように、符号は単純に最小値と最大値の合計から得られます。絶対値が大きい(反対の)方が勝ちです。

しかし、このアイデアはさらに活用できます。最小/最大の合計は、すべての要素の合計から真ん中の要素を差し引いたものと同じです。これは、最大3回の比較でわかります。

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つの等しい値がある場合は、慎重に計画する必要があると思います)。

そしてさらに:

x = abs(b)>abs(a)?b:a;
return sign(x+c);

おそらくさらに:

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);  
于 2012-11-26T15:03:39.743 に答える
0

あなたの数が離散的で、適切な間隔であり、限られたサブセットから引き出されている場合、他の可能性があります。

たとえば、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であることに注意してください。したがって、1回乗算してX ^ 2を取得し、それを単独で乗算してX ^ 4を取得してから、Xを乗算します。 -合計3つの乗算。符号を保持するには、奇数の指数が必要です。

于 2012-11-26T16:40:25.293 に答える