タスクベースのプログラミング

パフォーマンスが重要な場合、マルチスレッド・プログラミングを行う方法として、スレッドのプログラミングは最適ではないことがあります。スレッドではなく、論理タスク の観点からプログラムを構築したほうが非常に有効です。これには、次のような理由があります。

次に、上記のポイントを詳細に説明します。

スレッドパッケージを使用して作成するスレッドは論理スレッド で、ハードウェアの物理スレッド にマップします。外部デバイスで待機しない計算の場合、最も効率が良いのは、1 つの物理スレッドで 1 つの論理スレッドが実行されているときです。その他の場合は、ミスマッチにより効率が低下します。物理スレッドが動作し続けるために十分な論理スレッドが実行されていないと、アンダーサブスクリプション が発生するか、効率が低下します。逆に、物理スレッドよりも多くの論理スレッドが実行されている場合、オーバーサブスクリプション が発生します。通常、オーバーサブスクリプションは、「タイムスライスのコスト」で説明されているように、オーバーヘッドの原因である論理スレッドのタイムスライス 実行を引き起こします。タスク・スケジューラーは、物理スレッドごとに 1 つの論理スレッドを割り当て、同じプロセスまたは他のプロセスの他のスレッドによる干渉を許容する方法で論理スレッドにタスクをマップして、オーバーサブスクリプションを回避しようとします。

論理スレッドに比べてタスクが優れている点は、タスクは論理スレッドよりも非常に軽い ことです。Linux* システムでは、タスクの開始と終了は、スレッドの開始と終了より 18 倍も速く処理されます。Windows® システムでは、この比率は 100 倍以上になります。この差は、スレッドにはレジスターやスタックなど自分自身の多くのリソースのコピーがあるためです。Linux* では、スレッドにはプロセス ID もあります。一方、インテル® TBB のタスクは小さなルーチンで、タスクレベルではスケジューラーでプリエンプトできません (関連する論理スレッドはオペレーティング・システムでプリエンプト可能です)。

インテル® TBB のタスクも、スケジューラーがアンフェア であるため、効率的です。スレッド・スケジューラーは、通常、ラウンドロビン方式でタイムスライスを分散します。この分散は、各論理スレッドに公平に時間が配分されるため "フェア" と呼ばれます。プログラムのハイレベルな構成が分からなくても使用できる最も安全な方法であるため、スレッド・スケジューラーはフェア (公平) です。タスクベースのプログラミングでは、タスク・スケジューラーは、オペレーティング・システムで利用できないより高いレベルの情報を持つため、効率化を優先して公平性を犠牲にする場合があります。確かに、スケジューラーは、効果が得られるまでタスクの開始を遅らせることがよくあります。

スケジューラーは、ロード・バランシング を均等にする役割を負います。適切な数のスレッドを使用することに加え、それらのスレッドに均等にワークを分散することが重要です。プログラムが十分に小さなタスクに分割されている限り、スケジューラーはロードバランスをとりつつスレッドにタスクを割り当てることができます。スレッドベースのプログラミングでは、ロードバランスはプログラマー自身が処理しなければならないため、正しく処理することは困難です。

ヒント

スレッド数以上のタスクを作成するプログラムを設計し、タスク・スケジューラーにタスクからスレッドへのマッピングを任せます。

最後に、スレッドの代わりにタスクを使用する主な利点は、タスクでは、よりハイレベルな、タスクベースのレベルで考えられることです。スレッドベースのプログラミングでは、効率性の観点から低レベルの物理スレッドで考えなければなりません。アンダーサブスクリプションやオーバーサブスクリプションを回避するには、物理スレッドごとに 1 つの論理スレッドを割り当て、論理スレッドが長時間ブロックされないようにする必要があります。さらに、比較的粗粒度のスレッドを処理する必要もあります。タスクを使用すると、効率的なスケジュールはスケジューラーに任せて、プログラマーはタスク間の依存性に集中できます。

関連情報