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

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

スレッド単位でプログラミングすることでパフォーマンスを追求するのは、マルチスレッド・プログラミングを行う方法としては適切ではない場合があります。いくつかの理由から、スレッドではなく論理タスクの観点からプログラムを作成する方がはるかに適切です。

  • 利用可能なリソースに合わせた並列処理

  • タスクの起動とシャットダウンの高速化

  • より効率的な評価順序

  • ロードバランスの改善

  • 高レベルの考察

次の節で、これらの詳細を説明します。

スレッドパッケージを使用して作成するスレッドは論理スレッドであり、ハードウェアの物理スレッドにマップされます。外部デバイスとの入出力待ちを伴わない計算主体の場合、最も効率がよいのは、1 つの物理スレッドで 1 つの論理スレッドが実行されているときです。それ以外の場合、ミスマッチにより効率が低下します。物理スレッドが動作し続けるのに十分な論理スレッドが実行されていない場合、アンダーサブスクリプションが発生するか、効率が低下します。オーバーサブスクリプションは、実行中の論理スレッドが物理スレッドよりも多いときに発生します。オーバーサブスクリプションは通常、論理スレッドのタイムスライス実行につながり、付録 A のタイムスライスのコストで説明されているようにオーバーヘッドが発生します。スケジューラーは、物理スレッドごとに 1 つの論理スレッドを割り当て、タスクを論理スレッドにマッピングすることで、オーバーサブスクリプションを回避しようとします。このマッピングは、同じプロセスまたは他のプロセスの他のスレッドによる干渉を許容する方式で行われます。

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

スケジューラーは不公平ですが、oneTBB 内のタスクは効率的です。スレッド・スケジューラーは、通常、ラウンドロビン方式でタイムスライスを分配します。各論理スレッドに公平に時間が配分されるため、この分散はフェアと呼ばれます。スレッド・スケジューラーは、プログラムの高レベルな構成を理解していなくても実行できる最も安全な方法であるため、通常はフェア (公平) です。タスクベースのプログラミングでは、タスク・スケジューラーはより高レベルの情報を保持しているため、効率のため公平性を犠牲にすることがあります。スケジューラーは、効果が得られるまでタスクの開始を遅らせることがしばしばあります。

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

ヒント

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

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