2015-12-27 5 views
5

ビットがすべての変数を移動するように、mビットで__m128i変数(たとえばv)をシフトする必要があります(結果変数はv * 2^mを表します)。 これを行う最善の方法は何ですか?__m128iをシフトする最も良い方法は?

別々V0シフトを_mm_slli_epi64とV1注:

r0 := v0 << count 
r1 := v1 << count 

のでV0の最後のビットは逃したが、私はR1にそれらのビットを移動したいです。

編集: Iコード、これより速く(M < 64)を探している:命令insrqextrqはシフト(および回転)するために使用することができるSSE4.Aで

r0 = v0 << m; 
r1 = v0 >> (64-m); 
r1 ^= v1 << m; 
r2 = v1 >> (64-m); 
+1

、あなたは運がいい。そうでなければ、それは醜い速くなり、実際には、実際には、シフト、AND、シャッフル、ORを行う必要があります。 –

+1

http://stackoverflow.com/questions/9980801/looking-for-sse-128-bit-shift-operation-for-non-immediate-shift-value –

+0

ビットストリーム、または算術変数(ints、浮動小数点など)? – bazza

答えて

1

__mm128i 1スルー-64ビットずつ。 8/16/32/64ビットのpextrN/pinsrXとは異なり、これらの命令は、0〜127の任意のビットオフセットでmビット(1〜64)を選択または挿入します。注意事項は、長さとオフセットの合計が128.

+0

改訂版の回答をご覧ください。正しい指示にpはありません。 –

+2

大きな注意点は、そのAMDだけであるように見えます。 –

3

コンパイル時定数シフトの場合、かなり良い結果が得られます。そうでなければ本当に。

これは他に明らかな方法がないため、質問のr0/r1コードのSSE実装です。可変カウントシフトは、ベクタ要素内のビットシフトでのみ使用でき、レジスタ全体のバイトシフトでは使用できません。そのため、低い64ビットを64まで高値まで持ち運び、可変カウントシフトを使用して正しい位置に配置します。

// untested 
#include <immintrin.h> 

/* some compilers might choke on slli/srli with non-compile-time-constant args 
* gcc generates the xmm, imm8 form with constants, 
* and generates the xmm, xmm form with otherwise. (With movd to get the count in an xmm) 
*/ 

// doesn't optimize for the special-case where count%8 = 0 
// could maybe do that in gcc with if(__builtin_constant_p(count)) { if (!count%8) return ...; } 
__m128i mm_bitshift_left(__m128i x, unsigned count) 
{ 
    __m128i carry = _mm_bslli_si128(x, 8); // old compilers only have the confusingly named _mm_slli_si128 synonym 
    if (count >= 64) 
     return _mm_slli_epi64(carry, count-64); // the non-carry part is all zero, so return early 
    // else 
    carry = _mm_srli_epi64(carry, 64-count); // After bslli shifted left by 64b 

    x = _mm_slli_epi64(x, count); 
    return _mm_or_si128(x, carry); 
} 

__m128i mm_bitshift_left_3(__m128i x) { // by a specific constant, to see inlined constant version 
    return mm_bitshift_left(x, 3); 
} 
// by a specific constant, to see inlined constant version 
__m128i mm_bitshift_left_100(__m128i x) { return mm_bitshift_left(x, 100); } 

これは、それが判明した以上に便利ではないと思いました。 _mm_slli_epi64は、カウントがコンパイル時定数ではない(整数regからxmm regまでmovdを生成する)場合でも、gcc/clang/iccで機能します。 _mm_sll_epi64 (__m128i a, __m128i count)iの欠如に注意してください)がありますが、少なくとも最近では、iの内在はpsllqのいずれかの形式を生成できます。


コンパイル時定数カウントバージョンは、(AVXなし又は5)、compiling to 4 instructionsをかなり効率的である。

mm_bitshift_left_3(long long __vector(2)): 
     vpslldq xmm1, xmm0, 8 
     vpsrlq xmm1, xmm1, 61 
     vpsllq xmm0, xmm0, 3 
     vpor xmm0, xmm0, xmm1 
     ret 

Performance:

これは3サイクルの待ち時間(vpslldqを有する(1) - > vpsrlq(1) - > vpor(1))をインテルSnB/IvB/Haswell上で実行し、スループットは2サイクルに1回に制限されます(ポート0のベクトルシフトユニットを飽和させる)。バイトシフトは、別のポートのシャッフルユニットで実行されます。イミディエイトカウントベクタシフトはすべてシングルuop命令ですので、これは他のコードと混在したときにパイプライン空間を占有するのは4つの融合ドメインuopsだけです。 (この機能の可変数のバージョンは、それが指示をカウントからの見た目より悪化しているように、可変数のベクトルシフトは、2 UOP、2サイクルのレイテンシがあります。)

または数> = 64のために:

mm_bitshift_left_100(long long __vector(2)): 
     vpslldq xmm0, xmm0, 8 
     vpsllq xmm0, xmm0, 36 
     ret 

シフトカウントがではなく、コンパイル時定数がでない場合、キャリーを左右シフトするかどうかを調べるにはcount> 64で分岐する必要があります。シフトカウントは符号なし整数として解釈されるので、負のカウントは不可能です。

また、intをカウントし、64カウントをベクトルレジスタに入れるには、余分な指示が必要です。これをベクトル比較とブレンド命令でブランチレスで行うことは可能かもしれませんが、ブランチはおそらく良い考えです。


GPレジスタの__uint128_tの可変数バージョンはかなり見えます。 SSEバージョンよりも優れています。 Clang does a slightly better job than gcc, emitting fewer mov instructionsですが、count> = 64の場合は2つのcmov命令が引き続き使用されます。 `palignr`: 'M'は、8ビットの倍数であることを起こる、あなたがSSSE3を持っている場合(。x86の整数シフト命令ではなく、飽和の、数をマスクしているため)

__uint128_t leftshift_int128(__uint128_t x, unsigned count) { 
    return x << count; // undefined if count >= 128 
} 
+0

ありがとうございます。残念ながら、 'count'はコンパイル時定数ではありません。しかし、私は両方の提案をテストします。 – user0

+0

私のテストによれば、4つの 'int64_t'変数で書かれた私の古いコードは、ランダムに生成された' count'の方が速い(> 2倍)。コンパイル時定数 'count'では、' mm_bitshift_left'は少なくとも1.5倍高速です。 – user0

+0

@ user0:私は驚いていません。実際のアプリでは、シフトカウントには少しの予測可能性があると思います。また、あなたのマイクロベンチではシフトだけをテストしましたか、それとも2つの他のベクトル組み込み関数の間の演算としてシフトをテストしましたか?その場合、 'int64_t'シフトはベクトルからGPへの値を取得して戻す必要があります。 (私は私の答えでは、あなたのデータがまだベクトルレジスタにない場合、 '__uint128'シフト(または' int64_t'と手書きで同等です)がうまくいくはずだと言いました) –

関連する問題