Agner Fogの最適化ガイドは優れています。彼は、ガイド、命令のタイミングの表、および最近のすべてのx86 CPU設計のマイクロアーキテクチャに関するドキュメントを持っています(Intel Pentiumまで遡ります)。/programming//tags/x86/infoからリンクされている他のリソースも参照してください。
楽しみのために、いくつかの質問に答えます(最近のIntel CPUの数値)。opsの選択は、コードを最適化する際の主要な要因ではありません(除算を回避できない場合を除く)。
CPUでの単一の乗算は加算よりも遅いですか?
はい(2の累乗でない限り)。(Intelのクロックスループットごとに1つだけで、レイテンシは3〜4倍です)。
基本的な数学および制御フローのオペコードの速度特性は正確に何ですか?
正確に知りたい場合は、Agner Fogの指示表とマイクロアーキテクチャガイドを参照してください。条件付きジャンプには注意してください。無条件ジャンプ(関数呼び出しなど)には多少のオーバーヘッドがありますが、それほどではありません。
2つのオペコードの実行に同じサイクル数が必要な場合、両方のパフォーマンスコードはパフォーマンスの向上/損失なしで互換的に使用できますか?
いいえ、彼らは他と同じ実行ポートをめぐって競争するかもしれませんし、競争しないかもしれません。CPUが並行して処理できる依存関係チェーンに依存します。(実際には、通常、有益な決定を下す必要はありません。IntelCPUの異なるポートで実行されるベクトルシフトまたはベクトルシャッフルを使用できる場合があります。しかし、レジスタ全体のバイト単位のシフト(PSLLDQ
など)シャッフルユニットで実行されます。)
x86 CPUのパフォーマンスに関して共有できるその他の技術的な詳細は歓迎します
Agner Fogのマイクロアーキテクチャドキュメントでは、IntelおよびAMD CPUのパイプラインについて、ループが反復ごとに必要なサイクル数を正確に計算するのに十分な詳細と、ボトルネックがuopスループット、依存関係チェーン、または1つの実行ポートの競合であるかを説明しています。同様に、StackOverflowの上の私の答えのいくつかを参照してください、この1またはこのいずれか。
また、 CPU設計が好きな場合は、http://www.realworldtech.com/haswell-cpu/(および以前の設計でも同様)を読んでください。
以下は、私のベストゲストに基づいて、Haswell CPU用にソートされたリストです。ただし、これは実際には、asmループのチューニング以外のことについて考える便利な方法ではありません。通常、キャッシュ/分岐予測効果が支配的であるため、適切なパターンを持つようにコードを記述してください。数値は非常に手作業で発生し、スループットが問題にならない場合でも高いレイテンシを考慮したり、他の事柄が並行して発生するためにパイプを詰まらせるuopをさらに生成したりします。特に キャッシュ/ブランチ番号は非常に構成されています。ループキャリーの依存関係ではレイテンシが重要であり、各反復が独立している場合はスループットが重要です。
TL:DRこれらの数値は、レイテンシ、実行ポートのボトルネック、フロントエンドスループット(またはブランチミスなどのストール)のトレードオフに関する限り、「典型的な」ユースケースについて私が描いているものに基づいて構成されています。 )。 深刻なパフォーマンス分析には、これらの数値を使用しないでください。
- 0.5〜1ビット単位/整数加算/減算/
シフトと回転(コンパイル時のconstカウント)/
これらすべてのベクトルバージョン(1サイクルあたり1〜4スループット、1サイクルレイテンシ)
- 1つのベクトル、最小、最大、比較-等しい、比較-より大きい(マスクを作成するため)
- 1.5ベクトルシャッフル。Haswell以降のシャッフルポートは1つしかありません。必要な場合はシャッフルを頻繁に行う必要があるので、シャッフルの使用を減らすことを検討するために、少し高めに重み付けしています。特に無料ではありません。メモリからpshufbコントロールマスクが必要な場合。
- 1.5ロード/ストア(L1キャッシュヒット。待ち時間よりスループットが向上)
- 1.75整数乗算(Intelでは1c tputあたり3cレイテンシ/ 1、AMDでは4c lat、2c tputあたり1つのみ)。LEAやADD / SUB / shiftを使用すると、小さな定数はさらに安価になります。しかし、もちろん、コンパイル時の定数は常に適切であり、多くの場合、他のものに最適化できます。(そして、ループ内の乗算は、コンパイラによっての
tmp += 7
代わりにループ内の強度が低下することがよくありますtmp = i*7
)
- 1.75いくつかの256bベクトルシャッフル(AVXベクトルの128bレーン間でデータを移動できるinsnの余分な遅延)。(または、車線の交差シャッフルがより多くのuopを必要とするRyzenでは3から7)
- 2 fp add / sub(および同じベクトルバージョン)(サイクルスループットあたり1または2、スループット3〜5サイクル)。遅延のボトルネックがある場合、たとえば
sum
変数が1つしかない配列を合計すると、遅くなる可能性があります。(ユースケースに応じて、この重みとfp mulを最小1または最大5に重み付けできます)。
- 2ベクトルfp mulまたはFMA。(FMAサポートを有効にしてコンパイルすると、x * y + zはmulまたはaddと同じくらい安くなります)。
- 2汎用レジスターをベクター要素に挿入/抽出します(
_mm_insert_epi8
など)
- 2.25 vector int mul(16ビット要素または8 * 8-> 16ビットを行うpmaddubsw)。Skylakeで安く、スカラーマルよりもスループットが良い
- 可変カウントによる2.25シフト/ローテート(2cレイテンシ、Intelでは2cスループットごとに1つ、AMDまたはBMI2では高速)
- 2.5分岐なしの比較(
y = x ? a : b
、またはy = x >= 0
)(test / setcc
またはcmov
)
- 3 int-> float変換
- 3つの完全に予測された制御フロー(予測された分岐、呼び出し、戻り)。
- 4ベクトルint mul(32ビット要素)(2 uops、Haswellで10cレイテンシ)
- 4整数除算または
%
コンパイル時定数(2のべき乗以外)。
- 7つのベクトル水平操作(たとえば
PHADD
、ベクトル内に値を追加)
- 11(ベクター)FP除算(10〜13cのレイテンシ、7cスループットあたり1つまたはそれ以下)。(めったに使用しない場合は安くなる可能性がありますが、スループットはFPマルチより6〜40倍劣ります)
- 13?制御フロー(不十分に予測された分岐、おそらく75%予測可能)
- 13 int除算(はい、FP除算よりも遅く、ベクトル化できません)(コンパイラは、mul / shift / addを使用して定数で除算し、魔法の定数を使用して除算します。div/ modの2の累乗は非常に安価です。)
- 16(ベクター)FP sqrt
- 25?ロード(L3キャッシュヒット)。(キャッシュミスストアはロードよりも安価です。)
- 50?FPトリガー/ exp /ログ。多くのexp / logが必要で、完全な正確さを必要としない場合、より短い多項式やテーブルで正確さをスピードと引き換えにできます。 SIMDベクトル化することもできます。
- 50-80? 常に予測ミスの分岐、コストは15〜20サイクル
- 200-400?ロード/ストア(キャッシュミス)
- 3000 ??? ファイルからページを読み込む(OSディスクキャッシュヒット)(ここで数値を作成)
- 20000 ??? ディスク読み取りページ(OSディスクキャッシュミス、高速SSD)(完全に構成された数)
推測に基づいてこれを完全に作り上げました。何かが間違っているように見える場合、それは私が別のユースケースを考えていたか、または編集エラーのためです。
AMD CPUの相対的なコストは、shift-countが可変の場合に高速の整数シフターを使用することを除いて同様です。AMD BulldozerファミリのCPUは、さまざまな理由で、ほとんどのコードでもちろん低速です。(Ryzenは多くのことをかなり得意としています)。
物事を一次元のコストまで煮詰めることは本当に不可能であることに留意してください。キャッシュミスと分岐の予測ミス以外に、コードブロックのボトルネックは、レイテンシ、合計uopスループット(フロントエンド)、または特定のポート(実行ポート)のスループットになります。
FP除算のような「遅い」操作は、周囲のコードが他の作業でCPUをビジーに保つ場合、非常に安価になる可能性があります。(ベクトルFP divまたはsqrtはそれぞれ1 uopです。遅延とスループットが悪いだけです。分割ユニットのみをブロックします。実行ユニット全体はブロックしません。整数divは数uopです。) 〜20 mulごとに追加し、CPUが実行する他の作業(独立ループの繰り返しなど)がある場合、FP divの「コスト」はFP mulとほぼ同じになる可能性があります。これはおそらく、実行しているすべての場合のスループットが低いが、合計uopsが低いため、他のコードと非常によく混ざり合っている(遅延が要因ではない場合)の最良の例です。
整数除算は、周囲のコードとそれほど友好的ではないことに注意してください。Haswellでは、8〜11cのスループットごとに1回、22〜29cのレイテンシで9 uopです。(Skylakeでも64ビットの除算ははるかに遅くなります。)レイテンシとスループットの数値はFP divにいくらか似ていますが、FP divは1つのuopにすぎません。
insnsの短いシーケンスをスループット、レイテンシ、および合計uopについて分析する例については、SOの回答の一部を参照してください。
他の人がこの種の分析を含むSO回答を書く場合、IDK。私は自分自身を見つけるのがはるかに簡単になりました。なぜなら、私はこの詳細に頻繁にアクセスすることを知っているからです。