特定のAssembly命令に必要なCPUサイクルを説明するIntelのオンラインの本があると聞きましたが、(一生懸命試した後)見つけることができません。 CPUサイクルを見つける方法を教えてもらえますか?
次に例を示します。以下のコードでは、mov/lockは1 CPUサイクルで、xchgは3 CPUサイクルです。
// This part is Platform dependent!
#ifdef WIN32
inline int CPP_SpinLock::TestAndSet(int* pTargetAddress,
int nValue)
{
__asm
{
mov edx, dword ptr [pTargetAddress]
mov eax, nValue
lock xchg eax, dword ptr [edx]
}
// mov = 1 CPU cycle
// lock = 1 CPU cycle
// xchg = 3 CPU cycles
}
#endif // WIN32
ところで:ここに私が投稿したコードのURLがあります: http://www.codeproject.com/KB/threads/spinlocks.aspx
パイプライン処理、アウトオブオーダー処理、マイクロコード、マルチコアプロセッサなどを考えると、アセンブリコードの特定のセクションが正確にx CPUサイクル/クロックサイクル/どのようなサイクルでもかかるという保証はありません。
そのような参照が存在する場合、特定のアーキテクチャを前提とした広範な一般化のみを提供でき、マイクロコードの実装方法によっては、Pentium MがAMDデュアルコアとは異なるCore 2 Duoと異なることがありますなど.
この記事は2000年に更新され、以前に書かれたものであることに注意してください。 Pentium 4でさえ、命令のタイミングに関して特定するのは困難です-PIII、PII、および元のペンティアムは簡単でした。参照されるテキストは、おそらく、より明確に定義された命令タイミングを持つ初期のプロセッサに基づいていました。
最近では、一般的にコードタイミングの推定に統計分析が使用されます。
最新のCPUで実行されているコードのパフォーマンスを正確に予測することは不可能であるという他の答えは真実ですが、それはレイテンシーが不明であることや、それらを知ることが役に立たないことを意味しません。
IntelおよびAMDのプロセッサーの正確なレイテンシーは Agner Fogの命令表 にリストされています。 Intel®64およびIA-32アーキテクチャ最適化リファレンスマニュアル 、および AMDおよびIntel x86プロセッサの命令レイテンシとスループット (Can BerkGüderの現在削除されたリンクのみ)回答)。 AMDは、公式の値を記載したPDFマニュアルもWebサイトに掲載しています。
タイトループを(マイクロ)最適化するために、各命令のレイテンシを知ることは、コードを手動でスケジュールしようとする際に非常に役立ちます。プログラマーは、コンパイラーができない多くの最適化を行うことができます(コンパイラーがプログラムの意味を変えないことを保証できないため)。
もちろん、これには、パイプラインの深さ、サイクルごとに発行できる命令の数、実行ユニットの数など、CPUに関する他の多くの詳細を知る必要があります。そしてもちろん、これらの数値はCPUによって異なります。ただし、多くの場合、すべてのCPUに対して多少なりとも動作する合理的な平均を算出できます。
ただし、このレベルで数行のコードを最適化するのは大変な作業であることに注意してください。そして、悲観的であることが判明したものを作るのは簡単です。最近のCPUは非常に複雑で、悪いコードから良いパフォーマンスを引き出すために非常に一生懸命に努力しています。しかし、彼らが効率的に処理できない場合、またはあなたが考える賢くて効率的なコードを作っている場合もあり、それはCPUを遅くすることが判明しました。
EditIntelの最適化マニュアルの表C-13を見ると、最初の列は命令タイプであり、各CPUIDのレイテンシの列がいくつかあります。 CPUIDは、番号が適用されるプロセッサファミリを示し、ドキュメントの別の場所で説明されています。レイテンシは、命令の結果が利用可能になるまでにかかるサイクル数を指定するので、これが探している数値です。
スループット列は、サイクルごとにこのタイプの命令をいくつ実行できるかを示しています。
この表でxchgを調べると、CPUファミリーによっては1〜3サイクルかかり、movは0.5〜1サイクルかかります。これらは、メモリを使用するlock xchg
ではなく、レジスタ間形式の命令用であり、非常に低速です。さらに重要なことは、レイテンシが大きく変動し、周囲のコードに与える影響(別のコアとの競合がある場合ははるかに遅い)であるため、ベストケースのみを見るのは間違いです。 (各CPUIDの意味を調べていませんが、0.5はPentium 4用であり、チップの一部のコンポーネントを2倍の速度で実行し、半サイクルで処理できると想定しています)
ただし、この情報を使用する予定は実際にはわかりませんが、コードが実行されている正確なCPUファミリがわかっている場合、レイテンシを合計すると、この一連の命令を実行するために必要な最小サイクル数がわかります。
最新のCPUは複雑な獣であり、パフォーマンス分析を困難にする他の手法の中でも パイプライン化 、 スーパースカラー実行 、および 順不同実行 を使用しています。 しかし不可能ではない!
命令のストリームのレイテンシを単純に足して合計ランタイムを取得することはできなくなりましたが、以下および以下で説明するように、コードの一部(特にループ)の動作の(多くの場合)非常に正確な分析を取得できます他のリンクされたリソース。
まず、実際のタイミングが必要です。これらはCPUアーキテクチャによって異なりますが、現在x86のタイミングに最適なリソースは、Agner Fogの 命令テーブル です。これらのテーブルには、thirty以上の異なるマイクロアーキテクチャーが含まれ、命令latencyがリストされます。入力から出力の準備ができました。アグナーの言葉では:
Latency:これは、命令が依存関係チェーンで生成する遅延です。数値は最小値です。キャッシュミス、ミスアライメント、および例外により、クロックカウントが大幅に増加する場合があります。ハイパースレッディングが有効な場合、他のスレッドで同じ実行ユニットを使用すると、パフォーマンスが低下します。非正規数、NAN、および無限大は、レイテンシを増加させません。使用される時間単位は、タイムスタンプカウンターによって指定される基準クロックサイクルではなく、コアクロックサイクルです。
したがって、たとえば、add
命令のレイテンシは1サイクルであるため、一連のdependent追加命令は、示されているように、1サイクルのレイテンシを持ちます。 add
ごと:
add eax, eax
add eax, eax
add eax, eax
add eax, eax # total latency of 4 cycles for these 4 adds
これは、add
命令がそれぞれ1サイクルしかかからないことを意味しないことに注意してください。たとえば、追加命令がnotに依存している場合、最新のチップでは、4つの追加命令すべてが同じサイクルで独立して実行できる可能性があります。
add eax, eax
add ebx, ebx
add ecx, ecx
add edx, edx # these 4 instructions might all execute, in parallel in a single cycle
Agnerは、相互スループットと呼ばれるこの潜在的な並列処理の一部をキャプチャするメトリックを提供します。
相互スループット:同じスレッド内の同じ種類の一連の独立した命令の、命令あたりのコアクロックサイクルの平均数。
add
の場合、これは0.25
としてリストされます。これは、最大4つのadd
命令がすべてのサイクルを実行できることを意味します(1 / 4 = 0.25
の相互スループットが得られます)。
スループットの逆数は、命令のpipelining機能のヒントにもなります。たとえば、ほとんどの最新のx86チップでは、imul
命令の一般的な形式のレイテンシは3サイクルであり、内部で1つの実行ユニットのみがそれらを処理できます(通常[add
-対応ユニット)。ただし、独立したimul
命令の長いシリーズで観測されるスループットは1 /サイクルであり、3のレイテンシを想定すると3サイクルごとに1ではありません。理由はimul
ユニットがパイプライン化:前の乗算が完了していなくても、start新しいimul
every cycleを開始できます。
つまり、一連のindependentimul
命令はサイクルごとに最大1つ実行できますが、一連のdependentimul
命令は3サイクルごとに1回だけ実行されます(次のimul
は前の命令の結果が準備できるまで開始できないため)。
そのため、この情報を使用して、最新のCPUで命令のタイミングを分析する方法を確認できます。
それでも、上記は表面を傷つけているだけです。これで、一連の命令(レイテンシまたはスループット)を見る方法が複数あり、どちらを使用するかが明確ではない場合があります。
さらに、特定の命令がCPU内で同じリソースを奪い合うという事実や、CPUパイプラインの他の部分の制限(命令のデコードなど)など、上記の数値では捕捉されない制限があります。レイテンシーとスループットを見るだけで計算するよりも全体的なスループット。さらに、メモリアクセスや分岐予測などの「ALUを超えた」要素があります。トピック全体を網羅しています。これらはほとんどがうまくモデル化できますが、手間がかかります。たとえば、ここに 最近の投稿 があります。ここでは、関連する要素のほとんどについて回答がある程度詳しく説明されています。
すべての詳細を網羅すると、このすでに長い回答のサイズが10倍以上大きくなるため、最適なリソースを示します。 Agner Fogには、Optimizing Asemblyguide があり、1ダースほどの命令でループの正確な分析を詳細にカバーしています。現在のバージョンのPDFの95ページから始まる「12.7ベクトルループのボトルネックの分析例」を参照してください。
基本的な考え方は、命令ごとに1行のテーブルを作成し、それぞれが使用する実行リソースをマークすることです。これにより、スループットのボトルネックを確認できます。さらに、実行される依存関係のループを調べて、それらのいずれかがスループットを制限していないかどうかを確認する必要があります(「12.16依存関係の分析」を参照)場合)。
手作業で行いたくない場合、Intelは Intel Architecture Code Analyzer をリリースしました。これは、この分析を自動化するツールです。現在、Skylakeを超えて更新されていませんが、マイクロアーキテクチャはあまり変更されていないため、タイミングは同等のままであるため、Kaby Lakeの結果は依然としてかなり妥当です。 この回答 は詳細に説明されており、出力例を提供します。 ユーザーズガイド は半分悪いわけではありません(ただし、最新バージョンに関しては古くなっています) 。
通常、Agnerは新しいアーキテクチャのリリース直後にタイミングを提供しますが、InstLatX86
およびInstLatX64
の結果で同様に編成されたタイミングについて instlatx64 をチェックアウトすることもできます。結果は多くの興味深い古いチップをカバーし、新しいチップは通常かなり早く現れます。結果は、アグナーのものとほとんど一致していますが、いくつかの例外があります。このページでは、メモリレイテンシなどの値も確認できます。
インテルから直接 IA32およびIntel 64最適化マニュアルでタイミング結果を取得することもできます付録C:命令の遅延とスループット。個人的には、より完全で、Intelマニュアルが更新される前に届くことが多く、スプレッドシートとPDFバージョン。
最後に、 x86タグwiki には、コードシーケンスのサイクルを正確に分析する方法の他の例へのリンクなど、x86最適化に関する豊富なリソースがあります。
上記の「データフロー分析」の種類をさらに詳しく知りたい場合は、 データフローグラフの概要 をお勧めします。
CPUサイクルの測定とカウントは、x86ではもう意味がありません。
まず、サイクルをカウントしているCPUを自問しますか? Core-2?アスロン? Pentium-M?原子?これらのすべてのCPUはx86コードを実行しますが、実行時間はそれぞれ異なります。実行は、同じCPUの異なるステップ間でも異なります。
サイクルカウントが意味をなす最後のx86は、Pentium-Proでした。
また、CPU内では、ほとんどの命令がマイクロコードにトランスコードされ、x86のように見えない内部実行ユニットによって順不同で実行されることも考慮してください。単一のCPU命令のパフォーマンスは、内部実行ユニットで使用可能なリソースの量に依存します。
したがって、命令の時間は、命令自体だけでなく、周囲のコードにも依存します。
とにかく:プロセッサごとのスループットリソース使用量と命令のレイテンシを推定できます。関連情報は、IntelおよびAMDのサイトで見つけることができます。
Agner FogのWebサイトには非常に素晴らしい要約があります。レイテンシ、スループット、およびuopカウントについては、手順表をご覧ください。それらを解釈する方法については、マイクロアーキテクチャPDFを参照してください。
ただし、CPUモデルを1つしか見ていない場合でも、xchg
- with-memoryのパフォーマンスは予測できません。 L1Dキャッシュで既にキャッシュラインがホットになっている競合のない場合でも、メモリバリアがいっぱいになると、その影響は周囲のコードの他のアドレスへのロードとストアに大きく依存します。
ところで-サンプルコードはロックのないデータ構造の基本的な構成要素なので、コンパイラの組み込み関数の使用を検討しましたか? win32では、intrin.hをインクルードし、_InterlockedExchangeなどの関数を使用できます。
コンパイラーは命令をインライン化できるため、実行時間を短縮できます。インラインアセンブラは、コンパイラにasmコード周辺の最適化を常に無効にするよう強制します。
ロックxchg eax、dword ptr [edx]
ロックは、すべてのコアのメモリフェッチのためにメモリをロックします。これには、一部のマルチコアで100サイクルかかることがあり、キャッシュラインもフラッシュする必要があります。また、パイプラインが停止します。だから私は残りについて心配しません。
したがって、最適なパフォーマンスは、アルゴリズムの重要な領域の調整に戻ります。
シングルコアでは、ロックを削除することでこれを最適化できますが、マルチコアでは必要です。