この記事は、インテル® デベロッパー・ゾーンに掲載されている「OpenMP Execution Environment Functions」 (http://goparallel.sourceforge.net/openmp-execution-environment-functions/) の日本語参考訳です。
OpenMP* 仕様には、omp.h ヘッダーファイルで宣言されたいくつかのランタイム関数が含まれています。これらのランタイム関数により、OpenMP* で現在のランタイムスレッド設定の設定/取得を行うことができます。この記事では、これらの関数のいくつかを説明します。
プログラムで OpenMP* を使用する場合、OpenMP* プラグマだけでなく、OpenMP* ランタイム・ライブラリーも利用できます。ランタイム・ライブラリーには、omp.h ヘッダーファイルで宣言された関数が含まれています。そのいくつかを見てみましょう。
OpenMP* 4.0 仕様では 40 の異なる関数が記載されています。すべてについて説明することはできないため、ここではいくつかを取り上げます。皆さんからご要望があれば、今後、残りの関数についても取り上げたいと思いますので、皆さんのご意見をお聞かせください。(以前に http://goparallel.sourceforge.net/accurately-time-parallel-loops-openmp/ (英語) で触れたように、これらのランタイム関数のうち 2 つはタイミングに関するものです。)
スレッド数
合計スレッド数に関連するランタイム関数は 2 つあります。1 つはスレッド数を取得するもので、もう 1 つはスレッド数を設定するものです。omp_get_num_threads() 関数はいつでも呼び出すことができ、スレッドチームで現在実行中のスレッド数を取得できます。一方、omp_set_num_threads 関数はスレッド数を設定します。ただし、現在のスレッド数を設定するわけではなく、以降の処理で使用されるスレッド数を設定します。通常、parallel 指示句で並列領域を宣言するときに num_threads 節を追加しますが、任意の場所でこの関数を呼び出すことで以降の並列領域で使用するスレッド数を動的に設定できます。
次のスレッドチームで利用可能な最大スレッド数を取得する場合は、omp_get_max_threads() を呼び出すことで、スレッド数の上限が分かります。
また、現在実行中のスレッドの番号が知りたい場合は、omp_get_thread_num() を呼び出します。
プロセッサー
コンピューターは、各コアを個別のプロセッサーと見なします。しかし、最近のプロセッサー・コアは通常、ハイパースレッディング・テクノロジーにより 2 スレッドをサポートすることができ、オペレーティング・システムは各スレッドを個別のプロセッサーと見なします。そのため、オペレーティング・システムで利用可能なプロセッサー数を確認すると、通常、コア数の 2 倍の値になります。OpenMP* の omp_get_num_procs 関数によって返されるのもこの値です。今、私はクアッドコア・マシンでこの記事をタイプしていますが、omp_get_num_procs() を呼び出すテストプログラムを実行してみたところ、予想どおり 8 が返されました。
入れ子構造の並列処理
プログラムで入れ子構造の並列処理を許可するかどうか選択できます。入れ子構造の並列処理とは、名前のとおり、並列領域の中に並列領域があるものです。入れ子を無効にしても、並列領域を入れ子構造にすることはできます。ただし、その場合、内側の並列領域でスレッド数が 2 以上になることはなく、内側の並列領域は、それを含むスレッドによって実行されます。(入れ子構造の領域を使用する場合、規則に従う必要があります。詳細は、OpenMP* 4.0 仕様のセクション 2.16 を参照してください。)
実行時に入れ子構造が許可されているかどうか確認するには、omp_get_nested() を呼び出します。入れ子構造を有効にするか、無効にするかを指定するには、omp_set_nested() を呼び出します。プログラムを実行する前に OMP_NESTED 環境変数を設定することで、初期値を制御できます。設定可能な値は true (有効) または false (無効) です。例えば、omp_get_nested() によって返される値を出力する簡単なプログラムを作成し、この環境変数の設定が関数の戻り値にどのように影響するか確認できます。
$ export OMP_NESTED=false $ ./a.out 0 $ export OMP_NESTED=true $ ./a.out 1 $
並列コードの特定
並列領域内で並列に実行しているかどうか確認するには、omp_in_parallel() を呼び出します (並列領域でスポーンされているスレッドが 1 つのみの場合、その領域は並列に実行されていません。) 並列に実行している場合は 1 が返されます。そうでない場合は 0 が返されます。次のようなコードを使って、テストすることができます。
... cout << omp_in_parallel() << endl << endl; #pragma omp parallel for for (int i=0; i<10; i++) { cout << omp_in_parallel() << endl; } return 0; }
cout はスレッドセーフではないため、このコードの出力は一貫していません。最初に並列に実行されていないことを示す 0 が出力され、その後は 1 か改行文字が出力されます。1 の場合、そのコードは並列に実行されていることを示します。
まとめ
ここでは、OpenMP* で利用可能ないくつかのランタイム関数について述べました。よく使用されるものだけを取り上げましたが、このほかにも多くのランタイム関数があります。皆さんからご要望があれば、今後、ほかの関数についても取り上げていきたいと思います。