8

SSE (SSE 4.2 まで) を使用して 8 ビットの符号なし比較を実行する最も効率的な方法を見つけようとしています。

私が取り組んでいる最も一般的なケースは、> 0U の比較です。

_mm_cmpgt_epu8(v, _mm_setzero_si128())                // #1

(もちろん、これは非ゼロの単純なテストと見なすこともできます。)

しかし、私はより一般的なケースにも多少興味があります。

_mm_cmpgt_epu8(v1, v2)                                // #2

最初のケースは、0 と比較して結果を反転するなど、さまざまな方法を使用して 2 つの命令で実装できます。2 番目のケースでは通常、3 つの命令が必要です。たとえば、両方のオペランドから 128 を減算し、符号付き比較を実行します。(さまざまな 3 つの命令ソリューションについては、この質問を参照してください。)

私が理想的に探しているのは、#1 の単一命令ソリューションと #2 の 2 つの命令ソリューションです。これらのどちらも可能でない場合、最新の Intel CPU (Sandy Bridge、Ivy Bridge、Haswell) で最も効率的な 2 つまたは 3 つの命令のさまざまな実装についての考えにも興味があります。


これまでのケース #2 の最適な実装:

    1. unsigned max と等しいかどうかを比較し、結果を反転します。

#define _mm_cmpgt_epu8(v0, v1) \ _mm_andnot_si128(_mm_cmpeq_epi8(_mm_max_epu8(v0, v1), v1), \ _mm_set1_epi8(-1))

2 つの算術命令 + 1 つのビット単位 = 1.33 スループット。

    1. 両方の引数の符号ビットを反転し (== 128 を減算)、符号付き比較を使用します。

#define _mm_cmpgt_epu8(v0, v1) \ _mm_cmpgt_epi8(_mm_xor_si128(v0, _mm_set1_epi8(-128)), \ _mm_xor_si128(v1, _mm_set1_epi8(-128)))

1 つの算術命令 + 2 つのビット単位 = 1.16 スループット。


上記のケース #2 の実装から派生した、ケース #1 の最適な実装:

  • 1.

#define _mm_cmpgtz_epu8(v0) \ _mm_andnot_si128(_mm_cmpeq_epi8(v0, _mm_set1_epi8(0)), \ _mm_set1_epi8(-1))

1 つの算術命令 + 1 つのビット単位 = 0.83 スループット。

  • 2.

#define _mm_cmpgtz_epu8(v0) \ _mm_cmpgt_epi8(_mm_xor_si128(v0, _mm_set1_epi8(-128)), \ _mm_set1_epi8(-128)))

1 つの算術命令 + 1 つのビット単位 = 0.83 スループット。

4

3 に答える 3

2

SIMD ライブラリからコードをコピーするという精神で、Agner Fog のVector Class Library (C++) がそれを行う方法を次に示します。

// vector operator >= : returns true for elements for which a >= b (unsigned)
static inline Vec16cb operator >= (Vec16uc const & a, Vec16uc const & b) {
#ifdef __XOP__  // AMD XOP instruction set
    return _mm_comge_epu8(a,b);
#else  // SSE2 instruction set
    return _mm_cmpeq_epi8(_mm_max_epu8(a,b),a); // a == max(a,b)
#endif
}

// vector operator <= : returns true for elements for which a <= b (unsigned)
static inline Vec16cb operator <= (Vec16uc const & a, Vec16uc const & b) {
    return b >= a;
}

// vector operator > : returns true for elements for which a > b (unsigned)
static inline Vec16cb operator > (Vec16uc const & a, Vec16uc const & b) {
#ifdef __XOP__  // AMD XOP instruction set
    return _mm_comgt_epu8(a,b);
#else  // SSE2 instruction set
    return Vec16cb(Vec16c(~(b >= a)));
#endif
}

// vector operator < : returns true for elements for which a < b (unsigned)
static inline Vec16cb operator < (Vec16uc const & a, Vec16uc const & b) {
    return b > a;
}

// vector operator ~ : bitwise not
static inline Vec16uc operator ~ (Vec16uc const & a) {
    return Vec16uc( ~ Vec128b(a));
}

ここで、ビットごとの not は次のように定義されます

// vector operator ~ : bitwise not
static inline Vec128b operator ~ (Vec128b const & a) {
    return _mm_xor_si128(a, _mm_set1_epi32(-1));
}
于 2015-11-20T21:57:33.863 に答える
2

>=私は2つの命令で行うためのアイデアを持っていました:

  • 符号なし飽和による減算
  • ゼロと比較する

しかし、それは役に立ちません>

また、ErmIg のSIMD ライブラリの回答 (max_epu8(a,b) -> cmpeq with a) とほとんど同じですが、レジスタをゼロにする必要があるため、さらに悪い結果になります。ただし、これは SSE4.1 ではなく SSE2 で機能します。 psubusbと同じポートで実行されますpminusb


この回答の以前のバージョンでb-aは、符号ビットが iff に設定されているという誤った考えがありましたa>b。しかし、実際には、テストが必要なものの左側にある 1 ビットです: キャリー フラグ/ビット (パック整数 SIMD には存在しません)。

pshufb(否定された結果) またはpblendvb(非 VEX バージョンのみの Skylake でシングル uop である可能性があります)を使用して、要素の残りの部分に符号ビットをブロードキャストすることについてのいくつかのアイデアについては、編集履歴を参照してください。

于 2015-11-21T01:48:01.243 に答える