taskq プラグマは、囲まれた作業 (タスク) 単位が実行される環境を指定します。最初に、taskq プラグマを実行するすべてのスレッド間の中から、1 つのスレッドが選択されます。概念的には、taskq プラグマは、選択したスレッドを実行する空のキューを生成し、次に、taskq ブロックの内側がコード・シングルスレッドで実行されます。他のすべてのスレッドは、この概念キューに作業がキューイングされるのを待ちます。task プラグマは、潜在的に異なるスレッドで実行される作業単位を指定します。taskq ブロック内に task プラグマが存在すると、task ブロックの内側のコードは、taskq に関連付けられている概念キューにキューイングされます。キューイングされたすべての作業が終了し、taskq ブロックの最後に達すると、概念キューはなくなります。
多くの制御構造体は、異なる作業反復と作業生成のパターンを表しているため、ワークキューイング・モデルで並列化可能です。一般的なケース
while ループの各反復における計算が独立している場合、ループ全体が taskq プラグマに対する環境になります。while ループ本体内の文が、task プラグマで指定される作業単位になります。while ループの条件と制御変数への修正は、task ブロックの外側に置かれ、データを制御変数に依存させるようシーケンシャルに実行されます。
C++ STL (標準テンプレート・ライブラリ) 反復子は、前述した while ループに非常に類似しています。STL にストアされるデータの演算は、すべてのデータに対する反復動作から独立しているからです。データが独立している演算の場合、その演算は、作業の反復がシーケンシャルであれば、並列に処理されます。このタイプの while ループ並列処理は、ループの標準 OpenMP* の for ループワークシェアリングを一般化したものです。for ループのワークシェアリングでは、ループの増分演算が反復子で、ループの本体が作業単位です。しかし、for ループ反復子の変数は、大抵クローズされたフォームを持つため、その変数は並列に処理することができ、シーケンシャルなステップを回避できます。
再帰関数もまた、並列に反復処理を指定するために使用できます。そのメカニズムは、sections プラグマを使用する並列処理を指定するのに類似していますが、より柔軟性があります。taskq と task プラグマ間に任意のコードを置くことができ、関数の再帰的ネストで、taskq キューの概念ツリーをビルドできるためです。 taskq プラグマの再帰的ネストは、ネストされた OpenMP 並列領域のように動作する OpenMP のワークシェアリング構造の概念を拡張したものです。 ネストされた並列領域のように、ネストされた各ワークキューイング・構造は新しいインスタンスで 1 つのスレッドに検出されます。 しかし主な相違点は、ネストされたワークキューイング・構造は新しいスレッドやチームを生成しないことです。むしろチームからスレッドを再利用します。 これにより、動的環境で、マルチ・アルゴリズム並列処理を容易にし、並列処理の各レベルでスレッドの数を確保する必要がなくなり、トップレベルだけで済ますことができます。 その時点から、大量の作業量が内側のレベルで発生すると、外側のレベルからアイドルスレッドは、その作業の終了を支援することができます。 例えば、1 つのスレッドに各インカミング・リクエストを処理させ、多くのスレッドがインカミング・リクエストを待機している状態は、サーバ環境では非常に一般的です。 特定のリクエストにおいて、スレッドが処理を開始する時点では、そのサイズが明らかではないことがあります。 スレッドが、ネストされたワークキューイング・構造を使用し、内側の構造が開始された後にリクエストのスコープが大きくなると、外側の構造のスレッドは、リクエストを終了するために内側の構造に簡単に移動できます。
ワークキューイング・モデルはシーケンシャルなセマンティクスを保持するように設計されているため、同期化は taskq ブロックのセマンティクスでは固有のものです。 taskq 構造を検出したスレッドの taskq ブロックの完了時に暗黙的なチームバリアがあるため、taskq ブロック内で指定されたすべてのタスクの実行が終了したことを確認できます。 この taskq バリアは、オリジナル・プログラムのシーケンシャル・セマンティクスを実行します。 OpenMP ワークシェアリング構造のように、依存性が存在しないこと、またはタスクブロック間、あるいはタスクブロックのコードと、タスクブロック外の taskq ブロックのコード間で依存性が適切に同期されることを確認する必要があります。
文法、セマンティクスおよび構文は、OpenMP* ワークシェアリング構造と同じように設計されています。OpenMP ワークシェアリング構造で有効な構文のほとんどは、ワークキューイング・プラグマでも、適切な意味を持ちます。
#pragma intel omp taskq [clause[[,]clause]...] structured-block |
clause (構文) は次のいずれかです:
private 構文は、taskq の variable-list にある各オブジェクトの default-constructed バージョンの private を生成します。囲まれた各タスクで、captureprivate も含みます。各変数に参照されるオリジナルのオブジェクトは、構造に入る時点で中間値を持ちます。構造の動的範囲内では、オブジェクトは変更してはいけません。そして、構造から出る時点で中間値を持ちます。
firstprivate 構文は、taskq の variable-list に各オブジェクトにある copy-constructed バージョンの private を生成します。囲まれた各タスクで、captureprivate も含みます。各変数に参照されるオリジナルのオブジェクトは、構造の動的範囲内では、変更してはいけません。そして、構造から出る時点で中間値を持ちます。
lastprivate 構文は、taskq の variable-list にある各オブジェクトの default-constructed バージョンの private を生成します。囲まれた各タスクで、captureprivate も含みます。各変数に参照されるオリジナルのオブジェクトは、構造に入る時点で中間値を持ちます。構造の動的範囲内では、オブジェクトは変更してはいけません。そして、タスクの実行が終了した後に、オブジェクトは囲まれた最後のタスクからのオブジェクトの値をコピー割り当てされます。
reduction 構文は、variable-list にある各オブジェクトの囲まれたタスク・構造で与えられた演算子を持つリダクション演算を実行します。 operator と variable-list は、OpenMP 仕様と同じように定義されます。
ordered 構文は、オリジナルのシーケンシャル実行順序で、囲まれた task 構造で、ordered 構造を実行します。 ordered と結合される taskq ディレクティブには ordered 構文がなければなりません。
nowait 構文は、taskq の最後にある暗黙的なバリアを削除します。 taskq 構造にキューされたすべての task 構造が処理される前に、スレッドは taskq 構造を終了できます。
#pragma intel omp task [clause[[,]clause]...] structured-block |
clause (構文) は次のいずれかです:
private 構文は、task の variable-list にある各オブジェクトの default-constructed バージョンの private を生成します。変数に参照されるオリジナルのオブジェクトは、構造に入る時点で中間値を持ちます。構造の動的範囲内では、オブジェクトは変更してはいけません。そして、構造から出る時点で中間値を持ちます。
captureprivate 構文は、task がエンキューされた時点で、task の variable-list に各オブジェクトにある copy-constructed バージョンの private を生成します。各変数に参照されるオリジナルのオブジェクトは、その値を保持しますが、task 構造の動的範囲内では、変更してはいけません。
#pragma intel omp parallel taskq [clause[[,]clause]...] structured-block |
clause (構文) は次のいずれかです:
clause (構文) は、上記の OpenMP の parallel 構造または taskq 構造と同様です。