1. ホーム
  2. c++

[解決済み] 32ビット整数のvblendはどうやるの?なぜ _mm256_blendv_epi32 はないのですか?

2022-01-29 07:15:24

質問

AVX2 x86 256bit SIMD 拡張を使用しています。私は32ビット整数成分的にif-then-else命令を実行したいです。Intelのドキュメントでは、このような命令はvblendと呼ばれています。

インテル® イントリンシック・ガイドには、関数 _mm256_blendv_epi8 が含まれています。この関数は私が必要とすることをほぼ実現してくれます。唯一の問題は、それが 8 ビット整数で動作することです。残念ながら、_mm256_blendv_epi32 はドキュメントに存在しません。最初の質問は、なぜこの関数が存在しないのでしょうか?2番目の質問は、どのようにエミュレートするかということです。

いくつかの検索の後、私は32ビット浮動小数点のために私が欲しいものを行う_mm256_blendv_psを見つけました。さらに、整数から32ビット浮動小数点へのキャストとその逆を行うキャスト関数 _mm256_castsi256_ps と _mm256_castps_si256 が見つかりました。これらをまとめると

inline __m256i _mm256_blendv_epi32 (__m256i a, __m256i b, __m256i mask){
    return _mm256_castps_si256( 
        _mm256_blendv_ps(
            _mm256_castsi256_ps(a),
            _mm256_castsi256_ps(b),
            _mm256_castsi256_ps(mask)
        ) 
    );
}

これは5つの関数のように見えますが、4つは単なるキャストで、1つはプロセッサ命令に直接マッピングされています。したがって、関数全体は1つのプロセッサ命令に集約される。

したがって、本当に厄介なのは、対応する組込み関数がないことを除けば、32ビットblendvがあるように見えることです。

この場合、何か失敗する境界線はあるのでしょうか?例えば、整数のビットパターンがたまたま浮動小数点のNANを表していた場合はどうなるのでしょうか?blendvはこれを単に無視するのでしょうか、それとも何らかのシグナルを発するのでしょうか?

これがうまくいった場合。8ビット、32ビット、64ビットのblendvはありますが、16ビットのblendvはないということでよろしいでしょうか?

解決方法は?

もし、あなたの mask はすでにオールゼロ/オール1 は32ビット要素全体に対して(例えば vpcmpgtd の結果) を使用します。 _mm256_blendv_epi8 を直接指定します。

私のコード は、blendv が最上位ビットのみをチェックすることに依存しています。 .

それなら、いい選択肢が2つありますね。

  • 各要素の上位ビットをブロードキャストし、算術右シフト31を使用して VPBLENDVB ( _mm256_blendv_epi8 ) ... すなわち VPSRAD mask=_mm256_srai_epi32(mask, 31) .

    VPSRADはIntel Haswellでport0に対して1-uopです。 (Skylakeではもっとスループットが高い: p01)。 もし、あなたのアルゴリズムがport0でボトルネックになる場合(例えば、整数の乗算やシフト)、これはあまり良いことではありません。

  • 使用方法 VBLENDVPS レイテンシーよりもスループットを重視するため。 すべてのキャストはコンパイラを満足させるためのものであり、VBLENDVPSは1つの命令であなたが望むことを正確に行うというのは正しいです。

    static inline
    __m256i blendvps_si256(__m256i a, __m256i b, __m256i mask) {
        __m256 res = _mm256_blendv_ps(_mm256_castsi256_ps(a), _mm256_castsi256_ps(b), _mm256_castsi256_ps(mask));
        return _mm256_castps_si256(res);
    }
    
    

    しかし インテルSnBファミリーのCPUは、バイパス遅延のレイテンシがある 整数の結果をFPブレンドユニットに転送する際に1サイクル、ブレンドの結果を他の整数命令に転送する際にさらに1cのレイテンシが発生します。 もし、これが長い依存関係の連鎖の一部でないなら、おそらくuopsを保存して、OoO execが余分なレイテンシを隠せるようにする方が良いでしょう。

バイパス遅延のレイテンシについては Agner Fogのマイクロアーク・ガイド . を作らないのは、そのためです。 __m256i FP 命令のイントリンシックです。 Sandybridge以降では、FPは しない は、PADDD のような命令から/への転送に余分なレイテンシがあります。 ですから、SHUFPS は、PUNPCK* または PALIGNR が正確にやりたいことをやらない場合、2 つの整数ベクターからデータを結合する素晴らしい方法です。 (整数のSHUFPSは、スループットがボトルネックである場合、Nehalemでも2cのペナルティがありますが、その価値はあります)。

両方の方法を試してベンチマーク . 周囲のコードによっては、どちらかの方法が良い可能性もあります。

uopのスループットや命令数に比べれば、レイテンシは重要ではないかもしれません。 また、結果をメモリに保存するだけであれば、ストア命令はデータがどのドメインから来たかを気にしないことに注意してください。

しかし、これを長い依存関係の連鎖の一部として使用する場合、クリティカルパスがマスクではなくブレンドされるデータを通過するのであれば、ブレンドされるデータの2サイクルの余分なレイテンシを避けるために命令を追加する価値があるかもしれません。

マスク生成がクリティカルパス上にある場合、VPSRADの1サイクルのレイテンシはバイパス遅延のレイテンシと等しいので、FPブレンドを使用すると、マスク->結果チェーンで1サイクル、データ->結果チェーンで2サイクルのレイテンシが余分になるだけであることに注意してください。 また、FPまたは整数ブレンドから効率的に転送できる命令でブレンド結果を消費する場合、FPブレンドを使用した方が、同じレイテンシで命令(とそのuop)を節約でき、純粋に勝利となります。


<ブロッククオート

例えば、整数のビットパターンがたまたま浮動小数点のNANを表していた場合、どうなるでしょうか?

BLENDVPSは気にしない。 インテルの インスナ・リフ・マニュアルには、命令でできること、できないことがすべて書かれています。 、および SIMD浮動小数点演算の例外。なし は、問題ないことを意味します。 を参照してください。 x86 タグ ウィキ は、ドキュメントへのリンク用です。

FPのblend/shuffle/bitwise-boolean/load/store命令は、NaNを気にしない。 実際のFP計算を行う命令(CMPPS、MINPSなどを含む)だけがFP例外を発生させ、デノーマルで遅くなる可能性があります。


<ブロッククオート

8bit、32bit、64bitのblendvはありますが、16bitのblendvがないということでよろしいでしょうか?

しかし、32ビットと16ビットの演算シフトがあるので、8ビット粒度のブレンドを使うには、せいぜい1命令余分にかかる程度です。 (PSRAQはないので、64ビット整数のblendvは、マスク生成がクリティカルパスから外れていたり、同じマスクがクリティカルパス上で何度も再利用されたりしない限り、BLENDVPDで行うのがベストな場合が多いです)。

最も一般的な使用例は、各要素がすでにオール1またはオール0であるコンペアマスクで、PAND/PANDN => PORでブレンドすることができます。 もちろん、マスクの符号ビットだけを真理値で残す巧妙なトリックは、特に変数ブレンドが3つのブールビット演算命令よりいくらか速いので、命令とレイテンシーを節約することができます。 (特に、変数ブレンドは、3つのブーリアンビット演算命令よりもいくらか高速です。(たとえば、2xCMPPSとマスクのOR演算の代わりに、2つのfloatベクトルが両方とも非負であるかどうかを確認するORPS。 これは、負のゼロを気にしない場合や、アンダーフローが発生しても -0.0 を負として扱います)。