はじめに
この連載は、OpenMPを通じて並列処理の考え方をお伝えすることを目的としています。マルチコアCPUが既に普及していますので、今後は並列プログラミングが一般的なものとなり、さまざまな技術が登場するでしょう。その時並列プログラミングの根底に流れる考え方を身に付けられていれば、比較的容易に新技術も習得することができるでしょう。
今回は並列プログラミングに役立つ実行時ライブラリの関数と並列ループについて解説します。この連載で解説に使用するサンプルコードはあくまでもOpenMPの基礎を理解するためのものであり、実務でOpenMPを使用する際にはよく理解してから用途に適したプログラミングを行ってください。
対象読者
筆者が想定している読者はCの基本的文法をマスターし、並列化プログラミングに興味を持っている方です。凝ったテクニックを極力さけ、基本的な文法さえ分かれば読めるように注意しますので、並列化に興味を持っている方はぜひこの連載に目を通してください。
必要な環境
必要な環境については、第1回を参照してください。
なお、この連載は基本的にWindows環境を想定して解説しますが、OpenMPそのものは他のOS上でも動作しますので、適宜読み替えて参考にしてください。
実行時ライブラリの概要
OpenMPでは、コンパイラが実装しなければならないAPI(アプリケーションプログラムインタフェース)を実行時ライブラリとして定義しています。従って、OpenMPをサポートするコンパイラであれば、共通してライブラリを使用することができ、移植性が高いソフトウェアを実装する手助けとなります。そして、実行時ライブラリは並列プログラミングを支援するための便利な関数を定義しています。
この連載では今後、OpenMPでの並列プログラミングを解説するために、並列処理に関連する情報を使用します。その情報は実行時ライブラリを使用することにより取得できますので、次項からよく使用する実行時ライブラリの関数について解説していきます。
並列プログラミングに関する関数と聞けば難しく感じるかもしれませんが、実行時ライブラリの使い方は実際にプログラムを動作させると比較的簡単に理解できますので安心して読み進めてください。
スレッドに関する関数
一番最初は、スレッドに関する情報を取得する実行時ライブラリの関数を解説します。並列プログラミングをする際には、スレッドに関する情報が取得できれば色々便利です。例えば、逐次プログラミングでは正常に動作しているプログラムが、OpenMPを適用した途端に予期しない処理結果を返すようになった時、どのスレッドが処理をしているのかや、動作しているスレッドの数が確認できればデバッグに役立ちます。
まずは記事に添付されているサンプルコードのうち、get_num_threadsプロジェクトを見てください。
#include <stdio.h> #include <omp.h> int main() { /* 逐次処理のスレッドに関する情報を表示 */ printf("現在使用中のスレッド数は「%d」です。\n", omp_get_num_threads() ); printf("使用可能なスレッド数は最大「%d」です。\n", omp_get_max_threads() ); /* 並列処理を指定した時のスレッドに関する情報を表示 */ #pragma omp parallel { #pragma omp single { printf("現在使用中のスレッド数は「%d」です。\n", omp_get_num_threads() ); printf("使用可能なスレッド数は最大「%d」です。\n", omp_get_max_threads() ); } } /* スレッド数と並列処理を指定した時のスレッドに関する情報を表示 */ #pragma omp parallel num_threads(10) { #pragma omp single { printf("現在使用中のスレッド数は「%d」です。\n", omp_get_num_threads() ); printf("使用可能なスレッド数は最大「%d」です。\n", omp_get_max_threads() ); } } printf( "\n" ); return 0; }
このサンプルプロジェクトは、omp_get_num_threads関数とomp_get_max_threads関数の使用法を示すものです。逐次処理をしている(parallelの指定をしていない)部分は、1つのスレッドしか使用しないのでomp_get_num_threads関数は必ず1を返します。一方、並列処理をした場合は、CPUの論理コア数と同じか、parallel構文などで指定した数と同じだけの数が返されます。そして、omp_get_max_threads関数は、必ずnthreads-var内部制御変数の値が返されます。num_threads指示句に指定された数は考慮されません。実際に実行して確認してみてください。
指示句というのはOpenMPの用語で、例えば#pragma omp parallel num_threads(10)の場合#pragma omp parallelの部分を指示文とよび、後続のnum_threadsの部分を指示句と呼びます。
なお、このサンプルコードで#pragma omp singleを指定している理由は、同じメッセージを何度も表示しないためです。single指示文がないと何度も同じメッセージが表示されることになります。single指示文をコメントアウトするなどして、一度試してみてください。
以上でスレッド数に関する情報が取得することができましたが、個々のスレッドが識別できれば大変便利です。幸いOpenMPのスレッドには番号が付けられ、この番号を取得する関数omp_get_thread_numが実行時ライブラリに用意されています。
サンプルプロジェクトomp_get_thread_numを用いて使用法を解説します。
#include <stdio.h> #include <omp.h> int main() { #pragma omp parallel printf("Hello, World (スレッド番号%d)\n", omp_get_thread_num() ); printf( "\n" ); return 0; }
このサンプルプロジェクトomp_get_thread_numを実行すると、複数のスレッドが動いていることと、そのスレッドの番号が確認できます。