Goのワークスティーリングスケジューラー

Goのスケジューラのタスクは、OSスレッド間で実行中のゴルーチンを配布することです。これは、1つ以上のプロセッサで実行できます。 マルチスレッドコンピューティングでは、ワークシェアリングとワークスチールの2つの計画パラダイムが発生しています。









スレッドの移行は、 ワークシェアリングよりもワークスチールアプローチの方が頻繁に発生しません。 すべてのプロセッサがビジーの場合、スレッドは移行されません。 アイドル状態のプロセッサが表示されるとすぐに、移行オプションが考慮されます。







バージョン1.1以降、Goスケジューラーは作業スチール方式に従って実装され、Dmitry Vyukovによって作成されました。 この記事では、ワークスティールプランナーデバイスとGoでの動作方法について詳しく説明します。







計画の基本



GoスケジューラはM:Nスキームに従って作成され、複数のプロセッサを使用できます。 いつでも、M個のゴルーチンは、最大GOMAXPROCSプロセッサーで実行されるN個のOSスレッド間で配布する必要があります。 Go Schedulerは、ゴルーチン、スレッド、およびプロセッサに次の用語を使用します。









次に、Pに固有の2つのキューがあります。各MをそのPに割り当てる必要があります。Pがブロックされているか、システムコールの終了を待っている場合、PにはMがない場合があります。 GOMAXPROCSプロセッサの最大数はいつでも可能です-P。いつでも、各Pに対して実行できるMは1つだけです。必要に応じて、スケジューラーによってMを超える数を作成できます。













各計画サイクルは、起動して実行する準備ができているゴルーチンの検索で構成されます。 各サイクルで、検索は次の順序で行われます。







runtime.schedule() { //  1/61   ,    G //   ,    //   , : //     P //   ,    //     ,  (poll)  }
      
      





すぐに実行できるGが見つかると、ロックされるまで実行されます。







注:グローバルキューにはローカルキューよりも利点があるように見えるかもしれませんが、グローバルキューの定期的なチェックは、Mがローカルキューのゴルーチンのみを使用しないようにするために重要です。







盗難(盗難)



新しいGが作成されるか、既存のGが実行可能になると、現在のPのすぐに実行可能なゴルーチンのローカルキューに配置されます。PはGの実行を完了すると、キューからGをプル(ポップ)しようとします。 リストが空の場合、Pは別のプロセッサ(P)をランダムに選択し、ゴルーチンの半分を自分のキューから盗もうとします。













上記の例では、P2は実行可能なゴルーチンを見つけることができません。 したがって、彼は誤って別のプロセッサー(P1)を選択し、3つのゴルチンを順番に盗みます。 P2はそれらを開始できるようになり、作業はプロセッサー間でより均等に分散されます。







紡績糸



スケジューラーは、すべてのプロセッサーを使用するために、できるだけ多くの実行可能なゴルーチンを常に多くのMに割り当てることを常に望んでいますが、同時に、CPUリソースとエネルギーを節約するために、非常に大食いのプロセスを中断(パーク)できる必要があります。 同時に、スケジューラは、多くの処理能力と優れたパフォーマンスを実際に必要とするタスクに対応できる必要があります。







絶え間ないプリエンプションは、パフォーマンスが最も重要な高性能プログラムにとって高価で問題があります。 ゴルーチンはOSスレッド間を絶えずジャンプするべきではないため、レイテンシが増加します。 すべてに加えて、システムコールが呼び出されると、スレッドは常にブロックおよびロック解除されなければなりません。 これは高価であり、オーバーヘッドが大きくなります。







これらのgoroutinジャンプを減らすために、Goスケジューラーはいわゆる回転スレッドを実装します。 これらのスレッドは、もう少し多くのプロセッサー能力を使用しますが、スレッドの混雑を減らします。 次の場合、スレッドはループしていると見なされます。









いつでも、最大でGOMAXPROCSのループMが存在する可能性があります。ループストリームが動作を見つけると、ループ状態を終了します。







他のMがPに割り当てられていない場合、Pに割り当てられたアイドルスレッドはブロックされません。新しいゴルーチンが作成されるか、Mがブロックされると、スケジューラーは少なくとも1つのループMがあることを確認して保証します。可能な場合は起動し、Mの不要なロック/ロック解除を回避します。







結論



Goスケジューラーは、スレッドの過剰な混雑を回避するために多くのことを行い、「スチール」メソッドによって未使用のプロセッサーにそれらを配布します。また、「ループ」スレッドを実装して、ブロッキングから非ブロッキング状態への頻繁な移行を回避します。







計画イベントは、 実行トレーサーを使用して監視できます。 特に、あなたの場合、プロセッサの使用が非効率的であると考えている場合は、スケジューラ内で発生するすべての問題の最下位に到達できます。







参照資料





提案やコメントがある場合は、 @rakyllと書いてください








All Articles