高レベルで、単一のスレッドが複数のコアでどのように実行されるかを理解しようとしています。以下が私の理解です。私はそれが正しいとは思わない。
私の Hyper-threading の読み取りに基づくと、OSはすべてのスレッドの命令を、それらが互いに待機しないように編成しているようです。次に、CPUのフロントエンドは、1つのスレッドを各コアに分散することによってこれらの命令をさらに編成し、開いているサイクル間で各スレッドからの独立した命令を分散します。
したがって、スレッドが1つしかない場合、OSは最適化を行いません。ただし、CPUのフロントエンドは、各コアに独立した命令セットを分散します。
https://stackoverflow.com/a/1593627 によると、特定のプログラミング言語は多かれ少なかれスレッドを作成する可能性がありますが、それらのスレッドで何を行うかを決定する際には無関係です。 OSとCPUがこれを処理するので、これは使用するプログラミング言語に関係なく発生します。
明確にするために、私は単一のコアで複数のスレッドを実行することについてではなく、複数のコアにわたって単一のスレッドを実行することについて質問しています。
私の概要の何が問題になっていますか?スレッドの命令は、どこでどのように複数のコアに分割されますか?プログラミング言語は重要ですか?これは幅広いテーマであることは知っています。私はそれについての高レベルの理解を期待しています。
オペレーティングシステムは、実行に適格なスレッドに time slice sのCPUを提供します。
コアが1つしかない場合、オペレーティングシステムは最も適格なスレッドをタイムスライスでそのコア上で実行するようにスケジュールします。タイムスライスが完了した後、または実行中のスレッドがIOでブロックされたとき、またはプロセッサが外部イベントによって中断されたとき、オペレーティングシステムは次に実行するスレッドを再評価します(同じスレッドをもう一度または別のスレッドを選択できます)。
実行の適格性は、公平性と優先度と準備のバリエーションで構成され、この方法により、さまざまなスレッドがタイムスライスを取得します。
複数のコアNがある場合、オペレーティングシステムは、最も適格なNスレッドをコアで実行するようにスケジュールします。
Processor Affinity は効率の考慮事項です。 CPUが以前とは異なるスレッドを実行するたびに、そのキャッシュは前のスレッドではウォームであるが、新しいスレッドではコールドであるため、少し遅くなる傾向があります。したがって、同じプロセッサ上で同じスレッドを多数のタイムスライスにわたって実行することは、効率上の利点です。
ただし、オペレーティングシステムは、異なるCPUで1つのスレッドのタイムスライスを自由に提供でき、異なるタイムスライスのすべてのCPUを循環できます。ただし、 @ gnasher729による のように、1つのスレッドを複数のCPUで同時に実行することはできません。
ハイパースレッディングは、単一のenhancedCPUコアが2つ以上の実行をサポートできるハードウェアのメソッドです異なるスレッドを同時に。 (このようなCPUは、追加のフルコアよりもシリコン不動産で低コストで追加のスレッドを提供できます。)この拡張されたCPUコアは、CPUレジスタ値など、他のスレッドの追加の状態をサポートする必要があり、調整状態と動作があります。スレッドを混同することなく、そのCPU内で機能ユニットを共有できます。
ハイパースレッディングは、ハードウェアの観点からもプログラマーの観点からも技術的に困難ですが、実行モデルは、より複雑なものではなく、単なる追加のCPUコアのモデルです。したがって、複数のハイパースレッドスレッドが1つのCPUコアのキャッシュアーキテクチャを共有しているため、新しいプロセッサアフィニティの問題がいくつかありますが、オペレーティングシステムは追加のCPUコアを認識します。
単純に、ハイパースレッドコアで実行されている2つのスレッドは、それぞれが独自のフルコアで実行される場合の半分の速度で実行されると考えるかもしれません。ただし、単一のスレッドの実行はスラックサイクルでいっぱいであり、その一部は他のハイパースレッドスレッドで使用できるため、これは必ずしもそうではありません。さらに、非スラックサイクルの間でも、1つのスレッドが他のスレッドとは異なる機能ユニットを使用している可能性があるため、同時実行が発生する可能性があります。ハイパースレッディング用に拡張されたCPUには、特にそれをサポートするために、頻繁に使用される特定の機能ユニットがいくつかあります。
単一のスレッドが複数のコアで同時に実行されるようなものはありません。
ただし、1つのスレッドからの命令を並行して実行できないという意味ではありません。それを可能にする命令パイプライン処理と順序外実行と呼ばれるメカニズムがあります。各コアには、単純な命令では利用されない冗長リソースがたくさんあるため、複数のそのような命令を一緒に実行できます(次の命令が前の結果に依存しない限り)。ただし、これはシングルコア内でも発生します。
ハイパースレッディングはこのアイデアの極端な変形であり、1つのコアが1つのスレッドからの命令を並列に実行するだけでなく、2つの異なるスレッドからの命令を混合して、リソースの使用をさらに最適化します。
関連するWikipediaのエントリ: 命令のパイプライン処理 、 順不同の実行 。
要約:シングルスレッドプログラムでの (命令レベル)並列処理 の検索と利用は、ハードウェアで純粋に実行されているCPUコアによって行われます。そして、数百の命令のウィンドウのみで、大規模な並べ替えではありません。
シングルスレッドプログラムは、マルチスレッドCPUからメリットを得ません。ただし、シングルスレッドタスクから時間を費やす代わりに、otherを他のコアで実行できます。
oSは、すべてのスレッドの命令を、それらが互いに待機しないように編成します。
OSはスレッドの命令ストリームの内部を調べません。コアにスレッドをスケジュールするだけです。
実際、各コアは、次に何をすべきかを理解する必要があるときに、OSのスケジューラー機能を実行します。スケジューリングは分散アルゴリズムです。マルチコアマシンをよりよく理解するには、各コアをカーネルを個別に実行していると考えてください。マルチスレッドプログラムと同様に、カーネルは、1つのコアのコードが他のコアのコードと安全に相互作用して、共有データ構造(実行準備ができているスレッドのリストなど)を更新できるように記述されています。
とにかく、OSは、マルチスレッドプロセスがエクスプロイトスレッドレベルの並列処理を利用する手助けに関与しており、手動でマルチスレッドプログラムを記述して明示的に公開する必要があります。 (または OpenMP などを使用した自動並列化コンパイラーによって)。
次に、CPUのフロントエンドは、1つのスレッドを各コアに分散することによってこれらの命令をさらに編成し、開いているサイクル間で各スレッドからの独立した命令を分散します。
CPUコアは、停止されていない場合(タイマー割り込みなどの次の割り込みまでスリープ状態)、1つの命令ストリームのみを実行しています。多くの場合、それはスレッドですが、カーネルがハンドラ割り込みまたはシステムコールを処理して中断した後、前のスレッドに戻る以外のことを実行することを決定した場合は、カーネル割り込みハンドラやその他のカーネルコードである可能性もあります。
ハイパースレッディングまたはその他のSMT設計では、物理CPUコアは複数の「論理」コアのように機能します。ハイパーコア付きクアッドコア(4c8t)CPUとプレーンな8コアマシン(8c8t)の間のOSの観点からの唯一の違いは、HT対応のOSがスレッドをスケジュールして物理コアを分離することです。 t互いに競争する。ハイパースレッディングを認識していないOSでは、8つのコアしか表示されません(BIOSでHTを無効にしない限り、4つしか検出されません)。
「フロントエンド」という用語は、マシンコードをフェッチし、命令をデコードし、それらをコアのアウトオブオーダー部分に発行するCPUコアの部分を指します。各コアには独自のフロントエンドがあり、全体としてはコアの一部です。フェッチする命令は、CPUが現在実行しているものです。
コアのアウトオブオーダー部分の内部では、入力オペランドが準備され、空き実行ポートがある場合、命令(またはuops)が実行ポートにディスパッチされます。これはプログラムの順序で発生する必要はないので、これはOOO CPUが 命令レベルの並列処理 を単一スレッド内で利用する方法です。
あなたの考えで「コア」を「実行ユニット」に置き換えれば、あなたは正しいに近いです。はい、CPUは独立した命令/ uopsを並列に実行ユニットに分配します。 (ただし、実際にはCPUの命令スケジューラ、つまり予約ステーションが実行準備の整った命令を選択するときに「フロントエンド」と言ったため、用語の取り違えがあります)。
アウトオブオーダー実行は、非常にローカルなレベルでのみ、ILPを見つけることができます。2つの独立したループ間ではなく、数百の命令までです(それらが短い場合を除く)。
たとえば、これに相当するasm
int i=0,j=0;
do {
i++;
j++;
} while(42);
intel Haswellでは、同じループとほぼ同じ速度でカウンタが1つだけ増加します。 i++
はi
の以前の値にのみ依存しますが、j++
はj
の以前の値にのみ依存するため、2つの依存関係チェーンは、プログラム順に実行されているすべての錯覚を壊すことなく、並列に実行できます。
X86では、ループは次のようになります。
top_of_loop:
inc eax
inc edx
jmp .loop
Haswellには4つの整数実行ポートがあり、それらすべてに加算ユニットがあるため、すべて独立している場合、クロックあたり最大4つのinc
命令のスループットを維持できます。 (latency = 1の場合、4つのinc
命令を実行中に維持することでスループットを最大化するために必要なレジスタは4つだけです。これをvector-FP MULまたはFMAと比較してください。latency= 5スループット= 0.5には10個のベクトルアキュムレータが必要ですスループットを最大化するために、10個のFMAを飛行状態に保ちます。各ベクトルは256bで、8個の単精度浮動小数点数を保持できます)。
分岐分岐のスループットもクロックごとに1つに制限されているため、分岐分岐もボトルネックです。ループは常に、反復ごとに少なくとも1つの全体クロックを必要とします。依存関係チェーンが長くなるeax
またはedx
の読み取り/書き込みも行わない限り、パフォーマンスを低下させることなく、ループ内にもう1つの命令を置くことができます。ループ内にさらに2つの命令(または1つの複雑なマルチuop命令)を配置すると、フロントエンドでボトルネックが発生します。これは、アウトオブオーダーコアに1クロックあたり4 uopsしか発行できないためです。 (4 uopsの倍数ではないループで何が起こるかについての詳細は this SO Q&A を参照してください:ループバッファーとuopキャッシュは興味深いものです。 )
より複雑なケースでは、並列処理を見つけるには、命令のより大きなウィンドウを調べる必要があります。 (たとえば、すべてが互いに依存する10個の命令のシーケンスがあり、次にいくつかの独立した命令がある場合があります)。
並べ替えバッファの容量は、順序が狂うウィンドウサイズを制限する要因の1つです。 Intel Haswellでは、192 uopsです。 (そして 実験的に測定する と、レジスタの名前変更容量(レジスタファイルサイズ)を併用することもできます。)ARMのような低電力CPUコアは、ROBがはるかに小さいサイズ、順序どおりに実行されない場合。
また、CPUはパイプライン処理されている必要があること、および順序が正しくないことにも注意してください。そのため、実行される命令よりもかなり前に命令をフェッチしてデコードする必要があります。できれば、フェッチサイクルを逃した後にバッファを補充するのに十分なスループットが必要です。ブランチがどの方向に進んだのかわからない場合、どこからフェッチすればよいのかわからないため、ブランチはトリッキーです。これが、分岐予測が非常に重要である理由です。 (そして最近のCPUが投機的実行を使用する理由:彼らは分岐がどの方向に進み、その命令ストリームのフェッチ/デコード/実行を開始するかを推測します。予測ミスが検出されると、最後の既知の良好な状態にロールバックし、そこから実行されます。)
CPUの内部についてもっと読みたい場合は、Stackoverflow x86 tag wiki へのリンクがあります Agner Fogのmicroarchガイド へのリンクと、David Kanterの詳細な図へのリンクIntelおよびAMD CPUの。彼の Intel Haswellマイクロアーキテクチャのまとめ から、これはHaswellコアのパイプライン全体(チップ全体ではない)の最終図です。
これは、単一CPUコアのブロック図です。クアッドコアCPUには、これらのチップが4つ搭載されており、それぞれに独自のL1/L2キャッシュ(L3キャッシュ、メモリコントローラー、およびシステムデバイスへのPCIe接続を共有)があります。
これは非常に複雑であることは知っています。 Kanterの記事では、たとえば、実行ユニットやキャッシュとは別にフロントエンドについて説明するために、この部分も示しています。