スケジュール・アルゴリズム

スケジューラーは、ワークスチール と呼ばれる手法を使用しています。各スレッドは、実行する準備ができているタスクの "レディプール" を保持します。レディプールは、スポーンされた (作成された) タスク・オブジェクトのデック (両端キュー) として構築されます。さらに、キューに入れられた タスク・オブジェクトの共有キューがあります。 タスクをスポーンすることとタスクをキューに入れることの違いは、スケジューラーがタスクを実行する際に影響します。

タスク t を完了した後、スレッドは下記の規則のうち最初に適用される規則に従って次のタスクを選択します。

  1. t.execute() で返されたタスク。
  2. t が最後に完了したプレデセッサーだった場合は t のサクセサー。
  3. スレッドの自身のデックの最後からポップされたタスク。
  4. スレッドに対するアフィニティーがあるタスク。
  5. 共有キューのほぼ最初からポップされたタスク。
  6. 別の無作為に選択されたスレッドのデックの最初からポップされたタスク。

スレッドがタスクをスポーン すると、そのタスクは自身のデックの最後にプッシュされます。上記のタスク規則 (3) はスレッドによって最も新しくスポーンされたタスクを取得しますが、規則 (6) は別のスレッドの最も古くスポーンされたタスクを取得します。

スレッドがタスクをキューに入れる と、そのタスクは共有キューの最後にプッシュされます。規則 (5) は以前キューに入れられたタスクの 1 つを取得し、キューに入れられるタスクを取得しません。これはスポーンされたタスクと対照的です。規則 (3) では、スレッドは自身の最も新しくスポーンされたタスクを取得します。

規則 (5) の "ほぼ" という表記に注意してください。スケーラビリティー上の理由により、共有キューは正確な FIFO (先入れ先出し) 動作を保証しません。厳密な FIFO (先入れ先出し) 動作が必要な場合は、個別のキューに実際のワークを入れた後、そのキューのワークを処理するタスクを作成してください。詳細は、「ノンプリエンプティブな優先度」を参照してください。

入れ子の並列処理では、スポーンとキューに入れる処理の違いを理解することが重要です。

一般に、キューに入れられたタスクを使用する明白な理由がなければ、スポーンされたタスクを使用してください。スポーンされたタスクは、参照の局所性、空間効率、および並列処理のバランスが最もよくとれています。スポーンされたタスクのアルゴリズムは、Cilk (Blumofe 1995) で使用されているワークスチールのアルゴリズムに似ています。ワークスチールの概念が発表されたのは、1980 年代のことです (Burton 1981)。スレッド・アフィニティーのサポートは、かなり後のことです (Acar 2000)。

関連情報