タスク・スケジューラーの実行ガイド#

デフォルトでは、タスク・スケジューラーは利用可能なすべての計算リソースを使用しようとします。場合によっては、タスク・スケジューラーを構成して、それらの一部のみを使用するように設定できます。

警告

タスク・スケジューラーの実行をガイドすると、構成可能性の問題が発生することがあります。

oneAPI スレッディング・ビルディング・ブロック (oneTBB) は、次の方法でアリーナ内でのタスク実行をガイドする task_arena インターフェイスを提供します。

  • 優先計算ユニットを設定する。

  • 計算ユニットの一部を制限する。

このようなカスタマイズは、task_arena::constraints 構造内にカプセル化されます。制限を設定するには、task_arena::constraints をカスタマイズし、構築時または初期化時にそれを task_arena インスタンスに渡します。

task_arena::constraints 構造を使用すると、次の制限を指定できます。

  • 優先 NUMA ノード

  • 優先コアタイプ

  • 1 つのコアあたりに同時にスケジュールされる論理スレッドの最大数

  • task_arena の同時実行レベル

tbb::info 名前空間のインターフェイスを使用して、tbb::task_arena::constraints インスタンスを構築できます。tbb::info 名前空間からのインターフェイスは、プロセス・アフィニティー・マスクを優先します。例えば、プロセスのアフィニティー・マスクによって一部の NUMA ノードでの実行が除外されている場合、これらの NUMA ノードは tbb::info::numa_nodes() インターフェイスによって返されません。

次の例では、これらのインターフェイスの使用方法を示します。

NUMA ノードの設定#

非均一メモリーアクセス (NUMA https://en.wikipedia.org/wiki/Non-uniform_memory_access システム) を備えたシステムで実行すると、1 つの NUMA ノードのスレッドが別の NUMA ノードに割り当てられたメモリーにアクセスすると、パフォーマンスが低下する可能性があります。このオーバーヘッドを削減するため、実行優先順位が異なる NUMA ノードに設定された複数の task_arena インスタンス間でワークを分割することができます。実行優先順位を設定するには、task_arena::constraints::numa_id フィールドに NUMA ノード識別子を割り当てます。

std::vector<tbb::numa_node_id> numa_indexes = tbb::info::numa_nodes(); 
std::vector<tbb::task_arena> arenas(numa_indexes.size()); 
std::vector<tbb::task_group> task_groups(numa_indexes.size()); 

for(unsigned j = 0; j < numa_indexes.size(); j++) { 
    arenas[j].initialize(tbb::task_arena::constraints(numa_indexes[j])); 
    arenas[j].execute([&task_groups, &j](){ 
        task_groups[j].run([](){/*いくつかの並行処理*/}); 
    }); 
} 

for(unsigned j = 0; j < numa_indexes.size(); j++) { 
    arenas[j].execute([&task_groups, &j](){ task_groups[j].wait(); }); 
}

コアタイプの設定#

インテル® ハイブリッド・テクノロジーを搭載したプロセッサーには、複数のコアタイプが含まれており、それぞれ異なる用途に適しています。ほとんどの場合、ハイブリッド CPU アーキテクチャーを備えたシステムでは、追加の API 呼び出しを必要とせず適切なパフォーマンスを発揮します。ただし、例外的なシナリオでは、優先コアタイプを設定することでパフォーマンスを調整できる場合があります。実行の優先コアタイプを設定するには、task_arena::constraints::core_type フィールドに特定のコアタイプ識別子を割り当てます。

この例では、最もパフォーマンスの高いコアタイプをワーク実行の優先コアタイプとして設定する方法を示します。

std::vector<tbb::core_type_id> core_types = tbb::info::core_types(); 
tbb::task_arena arena( 
    tbb::task_arena::constraints{}.set_core_type(core_types.back()) 
); 

arena.execute( [] { 
    /*最もパフォーマンスの高いコアタイプを優先として定義*/ 
});

同時にスケジュールされるスレッドの最大数を 1 つのコアに制限#

インテル® ハイパースレッディング・テクノロジーを搭載したプロセッサーでは、各コアで複数のスレッドを同時に実行できます。ただし、コアごとに同時に実行されるスレッドの数を減らす必要がある場合があります。このような場合、task_arena::constraints::max_threads_per_core フィールドに必要な値を割り当てます。

この例では、各コアで一度に 1 つのスレッドのみを実行する方法を示します。

tbb::task_arena no_ht_arena( tbb::task_arena::constraints{}.set_max_threads_per_core(1) ); 
no_ht_arena.execute( [] { 
    /*並列ワーク*/ 
});

コア上で実行されるスレッド数を制限するより容易な構成の方法は、tbb::task_arena の最大同時実行性を設定することです。

int no_ht_concurrency = tbb::info::default_concurrency( 
    tbb::task_arena::constraints{}.set_max_threads_per_core(1) 
); 
tbb::task_arena arena( no_ht_concurrency ); 
arena.execute( [] { 
    /*並列ワーク*/ 
});

前の例と同様に、アリーナ内のスレッド数は、使用可能なコア数と等しくなります。そして、この方法では、制約の少ない実行を強制することで、オーバーヘッドが少なくなり、構成の容易性が向上します。