GMPスケジューリング11シナリオの完全な分析

再版に基づいて追加

(1)シーン1

PはG1を所有しています。M1はPを取得した後にG1の実行を開始します。G1はgofunc()を使用してG2を作成します。局所性のために、G2は優先的にP1のローカルキューに追加されます。
ここに画像の説明を挿入

(2)シーン2

G1の実行後(関数:goexit)、Mで実行されているゴルーチンはG0に切り替えられ、G0はスケジューリング中のコルーチンの切り替えを担当します(関数:schedule)PのローカルキューからG2を取得し、G0からG2に切り替えて、G2の実行を開始します(機能:実行)。スレッドM1の再利用を実現します。
ここに画像の説明を挿入
各MにはG0があり、バインドされたPローカルキューでGをスケジュールします。

(3)シーン3

各Pのローカルキューは3Gしか格納できないと想定します。G2は6つのGを作成したいと考えており、最初の3つのG(G3、G4、G5)がp1のローカルキューに追加され、p1のローカルキューがいっぱいです。
ここに画像の説明を挿入

(4)シーン4

G2がG7を作成したとき、P1のローカルキューがいっぱいであり、ロードバランシングが必要であることがわかりました(P1のローカルキューのGの前半、および新しく作成されたG 转移到全局队列)

(実装では必ずしも新しいGである必要はありません。GがG2の後に実行されると、ローカルキューに保存され、古いGが新しいGの代わりに使用されてグローバルキューに参加します)

ここに画像の説明を挿入
これらのGがグローバルキューに転送されると、注文が中断されます。したがって、G3、G7、G4はグローバルキューに転送されます。

(5)シーン5

G2がG8を作成するとき、P1のローカルキューはいっぱいではないため、G8はP1のローカルキューに追加されます。
ここに画像の説明を挿入
G8がポイントP1でローカルキューに追加される理由は、P1が現在M1にバインドされており、G2が現在M1によって実行されているためです。したがって、G2によって作成された新しいGは、最初にそれ自体のMにバインドされたPに配置されます。

(6)シーン6

ルール:Gを作成するとき、Gを実行すると、PとMの他のアイドル状態の組み合わせ(アイドル状態のPがあることを前提としている必要があります)をウェイクアップして実行しようとします。
ここに画像の説明を挿入
G2がM2をウェイクアップし、M2がP2をバインドし、G0を実行するとしますが、P2のローカルキューにはGがなく、M2は現時点では回転しているスレッドです(Gはなく、実行中のスレッドであり、常にGを探しています)。

自旋线程:
M和P是绑定的状态,但P的本地队列为空,在运行G0。这时,M称为自旋线程
一般来说,自旋线程状态是短暂的一个过度状态,因为会很快从全局队列中获取G,或者全局队列为空时,触发work stealing机制。当任何地方都没有了G,也就意味着程序结束,组合绑定解除,P和M被销毁

当P的本地队列有G时,运行的G0被调度执行别的G时,自旋状态解除,称为非自旋状态

(7)シーン7

M2は、グローバルキュー( "GQ")からGのバッチを取得し、それをP2のローカルキューに配置しようとします(関数:findrunnable())。M2がグローバルキューから取得したGの数は、次の式に従います。

n = min(len(GQ)/GOMAXPROCS + 1, len(GQ/2))

グローバルキューから少なくとも1gを取得しますが、毎回グローバルキューからpローカルキューに移動しすぎないようにして、他のpにいくつかのポイントを残します。これは、グローバルキューからPローカルキューへの負荷分散です。
ここに画像の説明を挿入
シーンに合計4つのPがあるとします(GOMAXPROCSが4に設定されている場合、Mは最大4つのPを使用できます)。したがって、M2はP2のローカルキューを1 Gのグローバルキュー(つまりG3)からのみ移動し、G0からG3への切り替えを完了して、G3を実行することしかできません。

(8)シーン8

G2がM1で実行されているとします。2ラウンド後、M2はグローバルキューからP2のローカルキューにG7とG4を取得し、操作を完了しました。P2のグローバルキューとローカルキューはどちらも空です。シーン8セクションの左半分。
ここに画像の説明を挿入
グローバルキューにGがない場合、mはワークスティーリングを実行する必要があります。Gを使用して他のPからGの半分を盗み、それを独自のPローカルキューに配置します。P2は、 P1のローカルキューの末尾からGの半分を取得します。この例では、Gの半分は1つのG8のみであり、P2のローカルキューに入れて実行します。

(9)シーン9

P1のローカルキューG5とG6は、他のMによって盗まれ、実行が完了しました。現在、M1とM2はそれぞれG2とG8を実行しています。M3とM4には実行するゴルーチンがありません。M3とM4は回転状態であり、彼らは常にゴルーチンを探しています。
ここに画像の説明を挿入
なぜm3とm4をスピンさせたいのですか?スピンは基本的に実行されており、スレッドは実行されていますがGを実行しないため、CPUの無駄になります。シーンを破棄してCPUリソースを節約してみませんか。CPUの作成と破棄も時間の浪費になるため、新しいゴルーチンが作成されたときにMがすぐに実行することを期待します。CPUを破棄してから作成すると、遅延が増加し、効率が低下します。もちろん、スピニングスレッドが多すぎるとCPUの無駄になると考えられるため、システムには最大でGOMAXPROCSスピニングスレッド(現在の例ではGOMAXPROCS = 4、合計4 P)と余分なスレッドがあります。それは何もしないで彼らを眠らせます。

(10)シーン10

スピニングスレッドとしてのM3とM4に加えて、アイドルスレッドとしてのM5とM6もあると仮定します(Pにバインドされていません。ここでは最大4つのPしか持てないため、Pの数は常にM> =である必要があります。 Pそれらのほとんどは実行する必要があるPプリエンプトするMです)、G8はG9を作成し、G8はブロッキングシステムコールを行い、M2とP2はすぐにバインド解除され、P2は次の判断を実行します:P2ローカルキューにGがある場合、グローバルキューGまたは空きMがある場合、P2はすぐにMをウェイクアップしてバインドします。それ以外の場合、P2は空きPリストに追加され、Mが使用可能なpを取得するのを待ちます。このシナリオでは、P2ローカルキューにG9があり、他のアイドルスレッドM5にバインドできます。

ここに画像の説明を挿入

(11)シーン11

G8がノンブロッキングシステムコールを行う場合、G8はG9を作成します。M2とP2はソリューションに関連付けられますが、M2 P2を記憶し、G8とM2を状態システムに呼び出します。G8とM2がシステムコールを終了すると、以前にバインドされたP2を取得しようとします。取得できない場合は、空きPを取得します。それでもない場合、G8は実行可能状態として記録され、グローバルに追加されます。キュー。M2はそうではありません。Pのバインディングは休止状態になります(GCの回復と破壊を待つ長期の休止)
ここに画像の説明を挿入

概要

要約すると、Goスケジューラーは非常に軽量でシンプルであり、goroutineのスケジューリング作業をサポートし、Goネイティブ(強力な)同時実行機能を提供するのに十分です。Goスケジューリングの本質は、実行のために多数のゴルーチンを少数のスレッドに割り当て、マルチコア並列処理を使用してより強力な並行性を実現することです。

おすすめ

転載: blog.csdn.net/csdniter/article/details/112028411