タスク・スケジューラー

インテル® TBB は、アルゴリズム・テンプレートとタスクグループを動かすエンジンであるタスク・スケジューラーを提供します。直接呼び出すことも可能です。タスク・スケジューラーは多くの処理を行うため、タスクの使用は多くの場合、スレッドの使用よりも単純で効率的です。

タスクは計算の論理ユニットです。スケジューラーは、物理スレッドにタスクをマップします。マッピングはノンプリエンプティブです。各タスクには execute() メソッドがあり、スレッドが execute() の実行を開始すると、execute() がリターンするまで、タスクはスレッドにバインドされます。その間、スレッドは、子タスクまたは入れ子の並列構造の完了を待っている場合のみほかのタスクを処理します。待機中に、スレッドは、このスレッドまたはほかのスレッドで作成された無関係なタスクを含む、利用可能なタスクを実行します。

タスク・スケジューラーは、計算主体の処理を並列化するように意図されています。タスク・オブジェクトはプリエンプティブにスケジュールされないため、スレッドがほかのタスクを処理できないときに、長い間スレッドをブロックする呼び出しを行ってはなりません。

注意

スケジューラーは利用可能なワーカースレッドに収まるように実際の並列処理を調整するため、「潜在的に」並列可能なタスクが「実際に」並列実行されるという保証はありません。例えば、単一のワーカースレッドが提供された場合、スケジューラーは並列処理を作成できません。さらに、生産タスクの実行中に消費タスクがこの作業を行う保証がないため、生産タスクと消費タスクの関係でタスクを使用することは安全ではありません。

潜在的な並列処理は通常、「分割/結合」パターンによって構成され、2 つの基本パターンがサポートされています。最も効率的なのは、プログラマーが明示的に "継続" タスクを構築する継続渡しです。親は子タスクを生成して、子が完了するときに実行する継続タスクを指定します。継続は親の先祖を継承します。その後、親タスクは終了します。つまり、子をブロックしません。続いて子が実行され、子 (またはその継続) が終了した後、継続タスクが実行を開始します。次の図は、一連のステップを示しています。各ステップの実行タスクは色付きで表示されています。

継続渡しスタイル

明示的な継続渡しは、タスクからスレッドのスタックを分離するので効率的です。しかし、プログラムはより難しくなります。2 つ目のパターンは、暗黙的な継続を使用する "ブロックスタイル" です。このパターンはパフォーマンスの点では効率が落ちますが、プログラムはより簡単です。このパターンでは、下記の図で示されているように、親タスクはその子が完了するまでブロックします。

ブロックスタイル

便利さには代償がつきものです。ブロックされた親タスクはスタックに残りますが、スレッドは別のタスクをスチールして実行することができます。連続したスチールとブロックはスタックを境界なしで拡張させます。この問題を解決するため、スケジューラーはスチールを停止するブロックされたスレッドを制約します。この制約は利用可能な並列処理を制限し、パフォーマンスに影響することがあります。

並列アルゴリズムやフローグラフのような、タスク・スケジューラーの上でビルドされた高レベルのインテル® TBB 構造は、再帰的な並列処理と並列構造全体の完了のブロックのために継続渡しを使用します。

関連情報