std::execution::sequenced_policy, std::execution::parallel_policy, std::execution::parallel_unsequenced_policy, std::execution::unsequenced_policy
From cppreference.com
| ヘッダ <execution> で定義 |
||
| class sequenced_policy { /* 未指定 */ }; |
(1) | (C++17以降) |
| class parallel_policy { /* 未指定 */ }; |
(2) | (C++17以降) |
| class parallel_unsequenced_policy { /* 未指定 */ }; |
(3) | (C++17以降) |
| class unsequenced_policy { /* 未指定 */ }; |
(4) | (C++20以降) |
1) 並列アルゴリズムのオーバーロードを明確にし、並列アルゴリズムの実行が並列化されないことを要求するために、一意の型として使用される実行ポリシー型。このポリシー(通常はstd::execution::seqとして指定される)で呼び出される並列アルゴリズムにおける要素アクセス関数の呼び出しは、呼び出し元のスレッドで不定順にシーケンスされる。
2) 並列アルゴリズムのオーバーロードを明確にし、並列アルゴリズムの実行が並列化される可能性があることを示すために、一意の型として使用される実行ポリシー型。このポリシー(通常はstd::execution::parとして指定される)で呼び出される並列アルゴリズムにおける要素アクセス関数の呼び出しは、呼び出し元のスレッド、または並列アルゴリズムの実行をサポートするためにライブラリによって暗黙的に作成されたスレッドのいずれかで実行することが許可される。同じスレッドで実行されるそのような呼び出しは、互いに不定順にシーケンスされる。std::threadまたはstd::jthreadによって作成された実行スレッドが並行順次進行保証を提供する場合、ライブラリによって作成された実行スレッドは並列順次進行保証を提供する。そうでない場合、提供される順次進行保証は実装定義である。注: 並列順次進行保証は、実行スレッドがあるステップを実行した場合、最終的に別のステップを実行することを保証し、スレッドがクリティカルセクションに入り、ロックを取得することを可能にする。これは、ロックを持っているスレッドが最終的に再度スケジュールされ、ロックを解放できるためである。
3) 並列アルゴリズムのオーバーロードを明確にし、並列アルゴリズムの実行が並列化、ベクトル化、またはスレッド間で移行(例えば、親横取りスケジューラによる)される可能性があることを示すために、一意の型として使用される実行ポリシー型。このポリシーで呼び出される並列アルゴリズムにおける要素アクセス関数の呼び出しは、未指定のスレッドで順序付けられていない方法で実行することが許可され、各スレッド内では互いに非シーケンスされる。このポリシーで呼び出される並列アルゴリズムにおける要素アクセス関数の呼び出しは、std::atomicおよびその他の並行性プリミティブを含む、標準ライブラリによって同期を行うと指定されているような、ベクトル化非安全な操作を呼び出すことは許可されない。std::threadまたはstd::jthreadによって作成された実行スレッドが並行順次進行保証を提供する場合、ライブラリによって作成された実行スレッドは弱い並列順次進行保証を提供する。そうでない場合、提供される順次進行保証は、並列アルゴリズムを呼び出すスレッドのものである。注: 弱い並列順次進行保証は、ステップを実行した実行スレッドのいずれかが最終的に別のステップを実行することを保証する。これは、ロックを持っているスレッドがロックを取得しようとしているスレッドが終了するまで再度スケジュールされない可能性があるため、スレッドがクリティカルセクションに入ったり、ロックを取得したりすることを許可しない。
4) 並列アルゴリズムのオーバーロードを明確にし、並列アルゴリズムの実行がベクトル化される可能性があること、例えば、複数のデータ項目に対して操作する命令を使用して単一スレッドで実行されることを示すために、一意の型として使用される実行ポリシー型。
これらの実行ポリシーのいずれかを使用して並列アルゴリズムを実行中に、要素アクセス関数の呼び出しが捕捉されない例外を介して終了した場合、std::terminateが呼び出されるが、実装は例外を異なる方法で処理する追加の実行ポリシーを定義してもよい。
[編集] 備考
並列実行ポリシーを使用する場合、データ競合とデッドロックを回避するのはプログラマの責任である。
int a[] = {0, 1}; std::vector<int> v; std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int i) { v.push_back(i * 2 + 1); // Error: data race });
std::atomic<int> x {0}; int a[] = {1, 2}; std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int) { x.fetch_add(1, std::memory_order_relaxed); while (x.load(std::memory_order_relaxed) == 1) { } // Error: assumes execution order });
int x = 0; std::mutex m; int a[] = {1, 2}; std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int) { std::lock_guard<std::mutex> guard(m); ++x; // correct });
非シーケンス実行ポリシーは、関数呼び出しが互いに「非シーケンス」(すなわち、割り込み可能)である唯一のケースである。C++の他のすべての状況では、それらは不定順シーケンス(割り込み不可)である。このため、ユーザーは、これらのポリシーを使用する際に、メモリの割り当てまたは解放、ミューテックスの取得、非ロックフリーのstd::atomic特殊化の使用、または一般的に「ベクトル化非安全」な操作(ベクトル化非安全な関数とは、他の関数と同期するものであり、例えばstd::mutex::unlockは次のstd::mutex::lockと同期する)を実行することは許可されない。
int x = 0; std::mutex m; int a[] = {1, 2}; std::for_each(std::execution::par_unseq, std::begin(a), std::end(a), [&](int) { std::lock_guard<std::mutex> guard(m); // Error: lock_guard constructor calls m.lock() ++x; });
実装が並列化またはベクトル化できない場合(例えば、リソースの不足のため)、すべての標準実行ポリシーはシーケンシャル実行にフォールバックすることができる。
[編集] 関連項目
| (C++17)(C++17)(C++17)(C++20) |
グローバル実行ポリシーオブジェクト (定数) |