MPI tutorial in Fortran - Update 13 Jul 2007

MPI Tutorial in Fortran

Ver.1.0, 21 Jan 2003, H.Matsufuru

はじめに

MPI(Message Passing Interface) とは、分散型の並列処理 プログラミングを行う上で、任意のプログラム処理を受け持つ単位となる プロセス操作や、そのプロセスの間でデータをやり取りするために用いる メッセージ通信操作の仕様標準だそうです。

要するに、並列計算プログラムを書く時の、いろいろなマシンに共通の 便利なルーチン集のようなものと私は理解しています。 標準として決められているのはインターフェイスの部分だけなので、 実装上のパフォーマンスは各計算機、OS、コンパイラーに依ります。

以下は、MPI を使って FORTRAN で並列計算用プログラムを書くための 最小限と思われる事項をまとめたものです。 MPI の解説は良いものが Web などで得られますが、 Fortran を例 に使った簡単な解説は見当たらなかったため、書いてみました。
Step 0 から 3 まであり、Step 1 - 3 では簡単な source code を例 にして解説を行います。 手近に MPI が使える環境がある人は動作を確認するとよいでしょう。

尚、このページでの実行結果は、大阪大学核物理研究センターの スーパーコンピューター NEC SX-4 の上で走らせたものです。


Step 0

プログラムを書く上での、基本的な考え方をまとめます。

実際には、以下に書くような条件よりも複雑な演算を行うことが出来ますが、 ここでは MPI を使って簡単な並列計算が出来るようになることを目標と しているので (及び著者の能力の問題により)、条件を単純化しています。

まず、一つのプログラムが、並列計算で使われる全ての PE (プロセッサー・ エレメント: 並列に演算を行うプロセッサーの一単位で、それぞれが 対応するメモリ空間を持つとする) で走る、ということに注意します。 これを "SPMD" (single-program-multiple-data) と呼びます。 即ち、同一の実行ファイルで、それぞれのプロセッサー・ユニット にあるデータ (PE 間では互いに異なる) を扱います。

それぞれの PE は、番号によって区別されます。
例えば、4 つの PE を用いて並列計算を行うとき、それぞれの PE 上で走っているプログラムは、自分の PE を、0-3 のある数に よって認識しています。 これらは互いに異なるので、他の PE とデータのやりとり (通信) が必要な 際には、相手の PE の番号を指定します。
この ID 番号は、MPI のサブルーチンを呼び出すことによって決定されます。

MPI は、複数のプロセッサー間での通信や同期などの動作が必要なときに 呼び出される、サブルーチンや関数などのフォーマットに対する規格を 定めています。

ローカルな (即ち他の PE との通信を必要としない) 演算については、 通常の FORTRAN (77 あるいは 90) の書式を使います。

データは、各 PE のメモリ上に、それぞれの配列を定義します。 プログラムは共通なので同じ配列名で表されていても、異なる PE 上の 配列は異なるデータです。 自分以外の PE 上のデータが必要な場合には、通信によって必要とする データをやりとりする必要があります。
(共有メモリで一つのプログラムの DO ループを分割するタイプの並列化 との大きな違いなので注意。)


Step 1: spells

Example source code は こちら ( test1.f )

このプログラムは、並列に動くそれぞれの PE からそれぞれ、 自分の ID 番号を添えて "Hello world !" と出力するものです。

まず、INCLUDE している file に注意します。

MPI のルーチンを動かすのに必要な変数の定義などが入ったファイルを コンパイル時に読み込みます。 (MPI ルーチンを呼び出すルーチンの中でのみ必要。)

次に、初めと終りには、「おまじない」が必要です。

これは MPI ルーチンを呼び出すために必要なものの準備と、 それらを最後にクリアするためです。 他の MPI ルーチンを CALL している箇所よりも前と後に、 それぞれ一度 CALL します。

"ierr" は . . . . . なんだったか忘れてしまいました... Error の際の何かでしょう。 Integer として定義してあれば、 ちゃんと動いてるときには気にしなくていいと思います。

Step 0 で述べたように、他の PE とデータのやりとりをするためには、 それぞれの PE に対して適当な番号が振られていなければなりません。 また、いくつの PE その計算に参加しているかを知る必要もあります。

これらの MPI ルーチンは、それぞれ三つの引数を必要とします。 初めの MPI_COMM_WORLD は、このままの形で使って下さい。 Nprod, Nid は、それぞれ INTEGER 変数で、 変数名は特に何でも構いません。

このプログラムをコンパイルして、例えば 4 PE で実行した場合、 次のような結果が標準出力に得られます:

実行結果は こうなります

これは NEC の SX-4 (阪大 RCNP) で 4 PE を使って走らせた場合です。 各 PE は標準出力へ "Hello ..." と書き出しますが、これは特に順番を 定めてはいないので、どの PE が先に書き出すかはその時々によります。 PE が ID 番号に従って順番に書き出すような方法は Step 2 で説明されます。


Step 2: one-to-all, all-to-one communications

Example source code は こちら ( test2.f )

このセクションでは、ある PE が持っているデータを他の PE に伝える 方法、あるいは、全ての PE の持っているデータを集める方法について 説明します。

例にした Source code では、 N, A(1), A(2) ( N は整数、A(1),A(2) は実数 ) の三つのパラメターを入力して、 各 PE (ID番号 Nid) で B = A(1) + A(2) * N * Nid に従って 実数 B を計算し、これを各 PE が出力したあとで、 その和を求める、という操作をします。
パラメターの値は第 0 ノードで読み、上記計算の結果は各ノードから 出力します。

ある PE から、すべての PE にデータを送るとき:

この例では、N というある整数 (サイズは従って 1) を、 第 0 ノードからすべてのノードへ渡しています。 実数 (倍精度) で、サイズ 2 の配列の場合には次のようになります。

出力の際には、各ノードが順番に自分のノードでの計算結果を 出力していきますが、これは DO ループの中のどこかで、 一度すべてのノードが同期を取る、ということによって出来ます。

同期をとる必要があるときには:

とします。 すべてのノードがこのコマンドに到達するまで、一旦待つことになります。

すべての PE の情報を集める場合にはつぎのようにします。

初めの変数は各ノードが送り出すもので、次の変数で受け取ります。 3 番目はこれらの変数のサイズ。 4 つ目は、これらの変数が倍精度の実数値であることを示しています。 その次は、各ノードから受け取った値 (B)を、 すべて足したものを Bsum とする、 という操作をすることを表しています。 従って、このコマンドの実行後には、各ノードでの B を 合計したものが Bsum に入っています。

MPI_Reduce には、MPI_SUM 以外にも、 次のような機能があります。

他にも論理演算などがいろいろあります。 詳細は MPI のマニュアルを参照のこと。

サンプルプログラムの実行結果は こうなります


Step 3: one-to-one communication

Example source code は こちら ( test3.f )

例えば、となり同士のノードで、あるいは離れたある二つのノード間などで、 データの通信が必要な場合があります。
これは特に、場の変数を扱っていて、相互作用がローカルな場合に、 空間を分割することによって並列化するような場合によく起ります。

このようなデータの交換の場合、
送る方のノードでは、

(Nup が送りたい先のノード)
受け取る方のノードでは (Ndn が受け取りたい相手のノード)
というコマンドをそれぞれ使います。
それぞれ、最初の引数が送る変数 (の先頭アドレス)、次がサイズで、 3 番目は変数の型です。 5 番目の数字は、データが迷子にならないための、「タグ番号」です。

常にデータを交換するという場合 (Send と Recv の両方をいつもセットで使う場合) には、 次のようなコマンドも用意されています。

これによって、上の send と receive の両方を一つのコマンドで 済ませることができます。

サンプルプログラムの実行結果は こうなります


Appendix: other commands

その他に、あると便利なコマンド。


これだけの MPI コマンドを知っていれば、かなりの科学計算での 数値計算プログラムの並列化が出来るのではないかと思います。 もちろん並列計算に適したアルゴリズムで、各ノードへの変数の分散と 計算の分割がシンプルな場合ですが。 (私はこれくらいしか使っていません。)

もっときちんと勉強したいという人は、もちろん MPI のマニュアルを 当たって下さい。

MPI に関するリンク:

日本語のテキスト

解りにくい点、間違いなどはこのページの作者: 松古栄夫 (hideo.matsufuru(at)kek.jp) [(at)は@に変換] までお寄せ下さい。

[21 Feb 2003]


Update history: