x86 uopsは正確にどのようにスケジュールされていますか?



How Are X86 Uops Scheduled

解決:

あなたの質問はいくつかの理由で難しいです:

  1. 答えは、世代ごとに大きく異なる可能性のあるプロセッサのマイクロアーキテクチャに大きく依存します。
  2. これらは、Intelが一般に公開していないきめ細かい詳細です。

それにもかかわらず、私は答えようとします...



リザベーションステーションで複数のuopsの準備ができている場合、それらはどのような順序でポートにスケジュールされますか?

それ したほうがいい 最も古い[下記参照]ですが、マイレージは異なる場合があります。 P6マイクロアーキテクチャ(Pentium Pro、2および3で使用)は、5つのスケジューラ(実行ポートごとに1つ)を備えたリザベーションステーションを使用しました。スケジューラーは、ディスパッチする準備ができているuopsのスキャンを開始する場所として優先ポインターを使用しました。これは疑似FIFOのみであったため、最も古い準備完了命令が常にスケジュールされているとは限らない可能性があります。 NetBurstマイクロアーキテクチャ(Pentium 4で使用)では、統合リザベーションステーションを廃止し、代わりに2つのuopキューを使用しました。これらは適切に折りたたまれた優先度キューであったため、スケジューラーは最も古い準備完了命令を取得することが保証されていました。コアアーキテクチャがリザベーションステーションに戻り、崩壊する優先度キューを使用しているという知識に基づいた推測を危険にさらしますが、これを確認するためのソースが見つかりません。誰かが決定的な答えを持っているなら、私はすべての耳です。



uopが複数のポートに移動できる場合(上記の例のaddとleaのように)、どのポートを選択するかはどのように決定されますか?

それを知るのは難しい。私が見つけた最高のものは、そのようなメカニズムを説明しているIntelの特許です。基本的に、冗長機能ユニットを持つポートごとにカウンターを保持します。 uopsがフロントエンドを離れてリザベーションステーションに移動すると、ディスパッチポートが割り当てられます。複数の冗長実行ユニットから決定する必要がある場合は、カウンターを使用して作業を均等に分散します。カウンターは、uopsがリザベーションステーションに出入りするときに、それぞれインクリメントおよびデクリメントされます。

当然、これは単なるヒューリスティックであり、完全な競合のないスケジュールを保証するものではありませんが、それでもあなたのおもちゃの例で機能することはわかりました。 1つのポートにしか送信できない命令は、最終的にスケジューラーに影響を与え、「制限の少ない」uopsを他のポートにディスパッチします。



いずれにせよ、特許の存在は必ずしもそのアイデアが採用されたことを意味するわけではありません(とはいえ、著者の1人はPentium 4の技術リーダーでもあったので誰が知っていますか?)

答えのいずれかに、uopsの中から選択する最も古いような概念が含まれている場合、それはどのように定義されますか? RSに納品されてからの年齢は?準備ができてからの年齢?絆はどのように壊れていますか?プログラムの注文が入ってくることはありますか?

uopsはリザベーションステーションに順番に挿入されるため、ここで最も古いものとは、実際にリザベーションステーションに入った時刻、つまりプログラム順に最も古いものを指します。

ちなみに、これらのIACAの結果は、実際のハードウェアのニュアンスを反映していない可能性があるため、一粒の塩で取り上げます。 Haswellには、というハードウェアカウンターがあります。 uops_executed_port これにより、スレッド内の何サイクルがポート0〜7へのuopsの問題であったかがわかります。たぶん、あなたはあなたのプログラムのより良い理解を得るためにこれらを活用することができますか?


これがSkylakeで見つけたもので、 uopsは、ディスパッチ時(つまり、実行のために送信された時点)ではなく、発行時(つまり、RSに発行されたとき)にポートに割り当てられます。 。港の決定が派遣時になされたことを私が理解する前に。

のシーケンスを分離しようとするさまざまなテストを行いましたに行くことができる操作を追加しますp0156およびポート0にのみ行くimul操作。典型的なテストは次のようになります。

mov eax、[edi] mov eax、[edi] mov eax、[edi] mov eax、[edi] ...さらに多くのmov命令moveax、[edi] mov eax、[edi] mov eax、[edi] mov eax、[edi] imul ebx、ebx、1 imul ebx、ebx、1 imul ebx、ebx、1 imul ebx、ebx、1 add r9、1 add r8、1 add ecx、1 add edx、1 add r9、1 add r8、1 ecxを追加、1 edxを追加、1 r9を追加、1 r8を追加、1 ecxを追加、1 edxを追加、1 mov eax、[edi] mov eax、[edi] mov eax、[edi] mov eax、[edi ] ...さらに多くのmov命令moveax、[edi] mov eax、[edi] mov eax、[edi] mov eax、[edi]

基本的には長いリードインがありますmov eax、[edi]命令、これはp23なので、説明で使用されているポートを詰まらせないでください(私も使用できたはずです)nop命令ですが、テストは少し異なります。nopはRSに発行しません)。この後に「ペイロード」セクションが続きます。ここでは4つで構成されていますimulと12追加してから、さらにダミーのリードアウトセクションを追加しますmov命令。

まず、hayestiが上記でリンクした特許を見てみましょう。彼は、ポート割り当ての負荷分散に使用される、ポートに割り当てられたuopsの総数を追跡する各ポートのカウンターについての基本的な考え方を説明しています。特許の説明に含まれているこの表を見てください。

ここに画像の説明を入力してください

このテーブルは、p0または特許で説明されている3ワイドアーキテクチャの問題グループの3uopsのp1。動作は依存することに注意してください グループ内のuopの位置について 、そして4つのルールがあること1カウントに基づいて、論理的な方法でuopsを分散させます。特に、グループ全体に使用率の低いポートが割り当てられる前に、カウントを+/- 2以上にする必要があります。

Sklakeでの「問題グループ内の位置」の問題の動作を観察できるかどうかを見てみましょう。単一のペイロードを使用します次のように追加します。

edx、1を追加します。位置0mov eax、[edi] mov eax、[edi] mov eax、[edi]

...そして、次のように4つの命令チャック内でスライドさせます。

mov eax、[edi] add edx、1;位置1mov eax、[edi] mov eax、[edi]

...など、問題グループ内の4つのポジションすべてをテストします2。これは、RSがいっぱいの場合(のmov命令)が、関連するポートのいずれにもポート圧力がない場合:

  • 最初手順を追加するにはp5またはp6、選択されたポートは通常、命令が遅くなるにつれて交互になります(つまり、偶数の位置に指示を追加しますp5と奇数の位置に移動しますp6)。
  • 二番目命令の追加もp56-2つのうち最初の1つが行かなかった方。
  • その後さらに指示を追加すると、バランスが取れ始めますp0156、p5とp6は通常先にありますが、全体的にかなり均一です(つまり、p56と他の2つのポートは成長しません)。

次に、負荷がかかるとどうなるかを見てみましたp1とimul操作、次に最初に操作の追加:

imul ebx、ebx、1 imul ebx、ebx、1 imul ebx、ebx、1 imul ebx、ebx、1 add r9、1 add r8、1 add ecx、1 add edx、1 add r9、1 add r8、1 add ecx 、1 edxを追加、1 r9を追加、1 r8を追加、1 ecxを追加、1 edxを追加、1

結果は、スケジューラーがこれをうまく処理することを示しています-すべてのimulは予定されていましたp1(予想どおり)、その後のいずれも指示を追加しましたp1、周りに広がっている代わりにp056。したがって、ここではスケジューリングがうまく機能しています。

もちろん、状況が逆転したとき、そして一連のimulは後に来る追加、p1には、追加のシェアがロードされます。imulsヒット。これは、発行時にポート割り当てが順番に行われた結果です。これは、「先読み」して確認するメカニズムがないためです。スケジュールするときにimul追加します。

全体として、スケジューラーはこれらのテストケースで良い仕事をするように見えます。

次のような小さくてタイトなループで何が起こるかについては説明していません。

R9の下で、R10の下で1つ、与える1 ebx、edx、12月1日ecxトップJNZ

と同じように 例4 私の質問では、このループは満たされるだけです2つあるにもかかわらず、サイクルの約30%でp0に行くことができるはずのサブ命令p0は 毎日 サイクル。p1とp6はオーバーサブスクライブされ、各反復で1.24 uopsを実行します(1が理想的です)。この回答の上部でうまく機能する例と悪いループの違いを三角測量することはできませんでしたが、試すべきアイデアはまだたくさんあります。

指示のない例に注意しました レイテンシーの違い この問題に苦しんでいないようです。たとえば、「複雑な」ポート圧力を持つ別の4uopループは次のとおりです。

トップ:サブr8、1 ror r11、2 bswap eax dec ecxjnzトップ

uopマップは次のとおりです。

instr p0 p1 p5 p6 sub X X X X ror X X bswap X X dec / jnz X

だからサブは常に行く必要がありますp15、と共有物事がうまくいく場合はbswap。彼らはします:

'./sched-test2'(2回の実行)のパフォーマンスカウンター統計:

999,709,142 uops_dispatched_port_port_0(+-0.00%)999,675,324 uops_dispatched_port_port_1(+-0.00%)999,772,564 uops_dispatched_port_port_5(+-0.00%)1,000,991,020 uops_dispatched_port_port_6(+-0.00%) + -0.00%)1,001,268,722サイクル:u(+-0.00%)

だから問題は 五月 命令の待ち時間に関連している(確かに、例の間には他の違いがあります)。これは、この同様の質問で出てきたものです。


1テーブルには5つのルールがありますが、0カウントと-1カウントのルールは同じです。

2もちろん、私はできません 承知しました ここで、問題グループは開始および終了しますが、4つの指示を下にスライドさせながら、4つの異なる位置をテストします(ただし、ラベルが間違っている可能性があります)。私もそうではありません 承知しました 問題グループの最大サイズは4です-パイプラインの初期の部分はより広いです-しかし、私はそうだと信じており、いくつかのテストはそれがそうであったことを示しているようです(4 uopsの倍数のループは一貫したスケジューリング動作を示しました)。いずれにせよ、結論はさまざまなスケジューリンググループサイズで成り立ちます。