20

異なるタイプの論理 SSE 組み込み関数に違いはありますか? たとえば、OR 演算を使用する場合、3 つの組み込み関数_mm_or_ps_mm_or_pdあり_mm_or_si128ます。私の質問:

  1. 1つまたは別の組み込みを使用することに違いはありますか(適切な型キャストを使用)。特定の状況で実行時間が長くなるなどの隠れたコストはありませんか?

  2. porこれらの組み込み関数は、3 つの異なる x86 命令 ( 、orps、 ) にマップされますorpd。Intelが同じことを行ういくつかの命令のために貴重なオペコードスペースを浪費している理由を知っている人はいますか?

4

3 に答える 3

19
  1. 1つまたは別の組み込みを使用することに違いはありますか(適切な型キャストを使用)。特定の状況で実行時間が長くなるなどの隠れたコストはありませんか?

はい、どちらか一方を選択するパフォーマンス上の理由がある場合があります。

1:整数実行ユニットの出力を FP 実行ユニットの入力にルーティングする必要がある場合、またはその逆の場合、余分な 1 サイクルまたは 2 サイクルのレイテンシ (転送遅延) が発生することがあります。128b のデータを多くの可能な宛先のいずれかに移動するには、大量のワイヤが必要になるため、CPU 設計者は、すべての可能な入力ではなく、すべての FP 出力からすべての FP 入力への直接パスのみを持つなどのトレードオフを行う必要があります。

this answer、またはバイパス遅延についてはAgner Fog のマイクロアーキテクチャ ドキュメントを参照してください。Agner のドキュメントで「Nehalem でのデータ バイパスの遅延」を検索してください。いくつかの良い実用的な例と議論があります。彼は、分析したすべてのマイクロアーチについてセクションを持っています。

ただし、異なるドメインまたは異なるタイプのレジスタ間でデータを渡すための遅延は、Nehalem よりも Sandy Bridge と Ivy Bridge の方が小さく、多くの場合ゼロです。-- Agner Fog のマイクロ アーチ ドキュメント

コードのクリティカル パス上にない場合、レイテンシは問題にならないことを忘れないでください ( Haswell/Skylake では、生成された値を後で使用する際に感染することがありますが、実際のバイパスのずっと後に :/)。クリティカル パスのレイテンシではなく、uop スループットがボトルネックである場合は、pshufd代わりに使用することで効果が得られます。movaps + shufps

2:この...psバージョンは、レガシー SSE エンコーディングの場合、他の 2 つよりも 1 バイト少ないコードを使用します。(AVX ではありません)。これにより、次の命令が異なる方法で整列されます。これは、デコーダーや uop キャッシュ ラインに影響を与える可能性があります。一般に、小さいほど、I キャッシュのコード密度が高くなり、RAM からコードがフェッチされ、uop キャッシュにパッキングされます。

3:最近の Intel CPU は、ポート 5 で FP バージョンのみを実行できます。

  • Merom (Core2) と Penryn: orpsp0/p1/p5 で実行できますが、整数ドメインのみです。おそらく、3 つのバージョンすべてがまったく同じ uop にデコードされました。そのため、クロスドメイン転送の遅延が発生します。(AMD CPU もこれを行います。FP ビット単位の命令は、ivec ドメインで実行されます。)

  • Nehalem / Sandybridge / IvB / Haswell / Broadwell: porp0/p1/p5 でorps実行できますが、port5 でのみ実行できます。p5 はシャッフルにも必要ですが、FMA、FP add、および FP mul ユニットはポート 0/1 にあります。

  • Skylake:どちらpororps サイクルあたり 3 スループットです。Intel の最適化マニュアルには、バイパス転送の遅延に関する情報がいくつかあります。FP 命令への/からの遅延は、uop が実行されたポートに依存します。(通常、FP add/mul/fma ユニットはポート 0 と 1 にあるため、ポート 5 のままです。) Intel のガイドよりも 1 サイクル遅くテストされた Haswell AVX/FMA レイテンシも参照してください。 「バイパス」レイテンシは、レジスタのすべての使用に影響を与える可能性があります。上書きされます。

SnB/IvB (AVX であり AVX2 ではない) では、AVX2 が必要なため、p5 のみが 256b の論理演算を処理する必要があることに注意してくださいvpor ymm, ymm。Nehalemがこれを行ったので、これはおそらく変更の理由ではありません.

賢明な選択方法:

コンパイラは必要に応じてporforを使用できることに注意してください_mm_or_pd。したがって、これの一部は主に手書きの asm に適用されます。ただし、一部のコンパイラは、選択した組み込み関数にある程度忠実です。

ポート 5 の論理演算スループットがボトルネックになる可能性がある場合は、FP データであっても整数バージョンを使用してください。これは、整数シャッフルまたはその他のデータ移動命令を使用する場合に特に当てはまります。

AMD CPU は常に論理演算に整数ドメインを使用するため、複数の整数ドメインを実行する必要がある場合は、ドメイン間のラウンドトリップを最小限に抑えるためにそれらを一度に実行してください。dep チェーンがコードのボトルネックではない場合でも、待ち時間が短いほど、リオーダー バッファーからクリアされる時間が短くなります。

FP add 命令と mul 命令の間で FP ベクトルのビットをセット/クリア/反転したいだけの場合は、...ps倍精度データであっても論理を使用してください。...psバージョンは 1 バイト短くなっています (AVX なし)。

...pdただし、組み込み関数でバージョンを使用するには、実用的/人的要因の理由があります。他の人間があなたのコードを読みやすいかどうかも重要な要素です。彼らは、実際には double であるのに、なぜあなたがデータを single として扱っているのか不思議に思うでしょう。__m128C/C++ 組み込み関数の場合、との間のキャストでコードを散らかして__m128dも意味がありません。(そして、AVX を使用せずにコンパイルすると、実際に 1 バイト節約される場合は、コンパイラがとにかくorpsforを使用することを願っています。)_mm_or_pd

insn アラインメントのレベルを調整することが重要な場合は、組み込み関数ではなく、asm を直接記述してください。(命令を 1 バイト長くすると、uop キャッシュ ライン密度および/またはデコーダーの調整が改善される可能性がありますが、プレフィックスとアドレッシング モードを使用すると、一般に命令を拡張できます)

整数データの場合は、整数バージョンを使用してください。1 命令バイトを節約することは、その間のバイパス遅延padddなどの価値がなく、整数コードはポート 5 をシャッフルで完全に占有したままにすることがよくあります。Haswell では、多くのシャッフル / 挿入 / 抽出 / パック / アンパック命令が、SnB/IvB の p1/p5 ではなく、p5 のみになりました。(Ice Lake は最終的に、より一般的なシャッフル用に別のポートにシャッフル ユニットを追加しました。)

  1. porこれらの組み込み関数は、3 つの異なる x86 命令 ( 、orps、 ) にマップされますorpd。Intelが同じことを行ういくつかの命令のために貴重なオペコードスペースを浪費している理由を知っている人はいますか?

これらの命令セットの歴史を見ると、私たちがどのようにしてここにたどり着いたかがわかります。

por  (MMX):     0F EB /r
orps (SSE):     0F 56 /r
orpd (SSE2): 66 0F 56 /r
por  (SSE2): 66 0F EB /r

MMX は SSE の前に存在したため、SSE ( ...ps) 命令のオペコードは同じ0F xxスペースから選択されたようです。次に、SSE2 の場合、...pdバージョンは opcode に66オペランド サイズ...psのプレフィックスを追加し、整数バージョン66は MMX バージョンにプレフィックスを追加しました。

および/またはを除外することもできましたが、そうではありませんでした。おそらく彼らは、将来の CPU 設計では異なるドメイン間の転送パスが長くなる可能性があるため、データに一致する命令を使用することはより大きな問題になると考えていました。別々のオペコードがありますが、AMD と初期の Intel はそれらをすべて int-vector として同じように扱いました。orpdpor


関連/ほぼ重複:

于 2015-07-05T17:22:41.297 に答える
7

Intel および AMD の最適化ガイドラインによると、op 型とデータ型を混在させると、CPU が特定のデータ型のレジスタの 64 ビット半分を内部的にタグ付けするため、パフォーマンスが低下します。これは、命令がデコードされ、uops がスケジュールされるため、主にパイプラインに影響を与えるようです。機能的には、同じ結果が得られます。整数データ型の新しいバージョンでは、エンコーディングが大きくなり、コード セグメントでより多くのスペースを占有します。したがって、コード サイズが問題になる場合は、エンコーディングが小さい古い ops を使用してください。

于 2010-08-20T19:36:04.517 に答える
3

3 つすべてが事実上同じであると思います。つまり、128 ビットのビット演算です。異なる形式が存在する理由は、おそらく歴史的なものですが、確かではありません。NaN がある場合など、浮動小数点バージョンに追加の動作がある可能性があると思いますが、これはまったくの当て推量です。通常の入力の場合、命令は交換可能のようです。

#include <stdio.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <xmmintrin.h>

int main(void)
{
    __m128i a = _mm_set1_epi32(1);
    __m128i b = _mm_set1_epi32(2);
    __m128i c = _mm_or_si128(a, b);

    __m128 x = _mm_set1_ps(1.25f);
    __m128 y = _mm_set1_ps(1.5f);
    __m128 z = _mm_or_ps(x, y);
        
    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    c = (__m128i)_mm_or_ps((__m128)a, (__m128)b);
    z = (__m128)_mm_or_si128((__m128i)x, (__m128i)y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);
    
    return 0;
}

ターミナル:

$ gcc -Wall -msse3 por.c -o por
$ ./por

a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000
a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000
于 2010-05-10T18:42:10.427 に答える