OpenACC において、アクセラレータ上で並列に処理する領域を指定するための Parallel Compute 構文には kernels 構文と、parallel 構文の二つが存在する。この二つの構文をどのように使い分けたら良いか、分からない方も多いと思う。この章では、この二つの構文について、もう少し詳しく説明することにする。両者の構文の特徴は、5章に詳細に説明しているので再度、確認していただきたい。ここで、両者の構文の大きな違いを簡単述べる。
-
kernels 並列領域とは、「コンパイラ」が、当該プログラムの並列化方法や性能加速のためのデバイスに対する並列マッピングを行うことを想定したものである。すなわち、「コンパイラ」は、当該領域の並列化可能性について調べて、可能であれば kernel を生成すべき場所を決定し、さらに適切な性能を出すためのループスケジューリングを行う。また、ユーザ自身がディレクティブを追加指定することにより、コンパイラが行う最適化事項よりも優先させ、明示的な指示を与えることも出来る。このように、基本的に「コンパイラ」が並列化のための依存性解析や並列化、さらに性能に関するスケジューリング等の責任を持つものが、kernels 構文である。
-
parallel 並列領域とは、「ユーザ」が、当該プログラムの並列化方法や性能加速のための並列マッピングを明示的に指示することを想定したものである。すなわち、「ユーザ」自身が、当該領域を並列化(依存性)について調べて、kernel を生成すべき場所(領域)を指定し、さらにループスケジューリングを行うための並列分割方法やその場所を指示することが必要となる。ユーザの責任で並列化すべき領域を指定すると言うことであり、これによって誤った計算結果が出たとしても、これはユーザ・プログラミングの責に因るものとなる。
kernels 構文領域は、コンパイラが並列化やループスケジューリングを自動で実施してくれるが、並列化の阻害要因があると、コンパイラは並列実行可能なカーネルを生成してくれない(逆に言えば、これが kernel 構文の使いづらい部分とも言える)。また、対象とするループ構造は、tightly nested loop とされている。一方、parallel 構文は、ユーザが並列で動作するプログラム領域をディレクティブで指定する。その領域の中は、Non-tightly nested loop であっても良いし、複数の nested ループが構成されていても良い。また、並列スレッドが冗長に実行するスカラ部分が存在していても良い。コンパイラは、ユーザが指定した領域を「一つのアクセラレータ上のカーネル」として並列で動作するようにコード化する。なお、その際に parallel 構文で忘れてならない重要なポイントを以下にピックアップする。
- 並列依存性がないことをユーザが保証しなければ使えないこと(誤った結果を生む)
- その領域内で work-sharing (真の並列)実行を行うループに対して loop ディレクティブで指示する必要があること(これがなければ性能が、加速しない)。OpenACC parallel 構文は、OpenMP と同様な work-sharing モデルであると言える
- 基本的に、work-sharing ループの終了時点でバリア同期は行わないプログラミングモデルであることを理解しておくこと。但し、Parallel 領域の最後で同期を行う。OpenMP の parallel 構文の場合は、個々のwork-sharing ループの終了時点で同期をとることがデフォルトの挙動であるため、この点が OpenACC の場合と大きく異なると言える。
This chapter has not been completed yet.