SQUIDで、IntelMPIを使ったノード間並列(分散メモリ並列)処理を行う方法を解説します。MPIを除く、Intelコンパイラでの最適化については、こちらのページをご参照ください。
MPIとは
-
MPIとは、分散メモリ並列処理におけるメッセージパッシング(複数のプロセス間でデータをやり取りするために用いるメッセージ通信操作)の標準規格のことです。ノード間をまたがる並列化が可能となるため、その分大きなメモリ空間が利用可能になります。ただし、ユーザ自身で処理の分割やノード間の通信を指示する必要があるため、プログラミングの負担がやや大きくなります。
CとFORTRANで記述方法がやや異なりますが、本稿ではFORTRANを中心に説明します。
主な機能
-
MPIは以下のような機能を持っています。
・プロセスの管理
プログラムの初期化や終了処理を行います
・1対1の通信
1プロセス対1プロセスの通信操作
・集団の通信
グループ内のプロセス全体が関係する通信操作
基本的な利用方法
MPIプログラミングにかかる負担の多くはプログラムの「設計」に関するものであり、MPIを利用する為に覚えることはそんなに多くありません。本ページに書かれた、基本的な内容を習得するだけでも、十分利用することが可能です。
以下にFortranで書かれたMPIプログラムを載せています。こちらを参考に説明します。
1 2 3 4 5 6 7 8 9 10 |
program sample include 'mpif.h' !! MPI用のインクルードファイル(必ず指定) integer ierr,myrank,nprocs call MPI_INIT(ierr) !! MPI並列 開始 call MPI_COMM_RANK(MPI_COMM_WORLD,myrank,ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs,ierr) print *,"HELLO WORLD myrank=",myrank,"(",nprocs,"processes)" call MPI_FINALIZE(ierr) !! MPI並列 ここまで stop end |
プログラムのハイライト部分(2行目、4行目-6行目、8行目)が、MPIプログラム(MPIサブルーチン及びMPIに関連する処理)になります。5行目-7行目は、MPIの処理をPrint出力するために挿入した処理になりますので、入力しなくても、並列処理としての動きは変わりません。
2行目はMPI用のインクルードファイルの指定です。MPIを利用する場合、必ず指定してください。
プログラム中で「MPI_INIT」がcallされてから、「MPI_FINALIZE」がcallされるまでの区間がMPI並列の対象となり、MPI_INITがcallされた時点から、プロセスが生成されます。4行目と8行目がそれぞれ該当し、5~7行目の処理がマルチプロセスで実行されます。
5行目と6行目の「MPI_COMM_RANK」と「MPI_COMM_SIZE」はMPI処理の情報を得るためのサブルーチンです。「MPI_COMM_SIZE」では、処理を分割している数(並列プロセス数)を取得します。「MPI_COMM_RANK」では、分割された処理(並列プロセス)ごとにつけられた番号(プロセスID)を取得します。
並列プロセス数は、実行コマンド入力時に指定します。
上記のプログラムを、4プロセスで実行した結果は下記のようになります。
HELLO WORLD myrank= 3 ( 4 processes)
HELLO WORLD myrank= 1 ( 4 processes)
HELLO WORLD myrank= 0 ( 4 processes)
HELLO WORLD myrank= 2 ( 4 processes)
基本的なサブルーチンについて
MPI_INIT (ierr)
MPIを起動するコマンドです。
必ず他のMPIサブルーチンより前にコールしてください。
引数 | |
---|---|
ierr | 正常終了時に0が格納されます。エラー時はそれ以外の値が格納されます。 |
MPI_FINALIZE (ierr)
MPIを終了するコマンドです。
必ず他のMPIサブルーチンより後にコールしてください。
引数 | |
---|---|
ierr | 正常終了時に0が格納されます。エラー時はそれ以外の値が格納されます。 |
MPI_COMM_SIZE(comm, size, ierr)
引数「comm」で指定されたコミュニケータ(グループ)に含まれるプロセスの数の合計を取得する。
通常は「comm」に「MPI_COMM_WORLD」というMPI全プロセスを表すコミュニケータを指定し、
全プロセス数を取得するために使うことが多いのですが、複数のコミュニケータを使うことで、
複雑な処理を行うことが可能です。
引数 | |
---|---|
comm | コミュニケータを指定します。前述の通り、通常はMPI全プロセスをあらわす「MPI_COMM_WORLD」を指定することが多いです。 |
size | 取得したプロセス数の合計が格納されます。 |
ierr | 正常終了時に0が格納されます。エラー時はそれ以外の値が格納されます |
MPI_COMM_RANK(comm, size, ierr)
引数「comm」で指定されたコミュニケータ(グループ)内でプロセスごとにつけられた番号を取得する。
この番号のことを「プロセスID」「rank」と呼びます。
MPI_COMM_RANKと同じく、通常は「comm」に「MPI_COMM_WORLD」というMPI全プロセスを表すコミュニケータを指定することが多いです。
引数 | |
---|---|
comm | コミュニケータを指定します。前述の通り、通常はMPI全プロセスをあらわす「MPI_COMM_WORLD」を指定することが多いです。 |
rank | commで指定されたグループ内のプロセスID |
ierr | 正常終了時に0が格納されます。エラー時はそれ以外の値が格納されます |
上記の例では、基本的なサブルーチンのみ用いておりますが、これら以外にも、さまざまなサブルーチンが用意されています。詳細は、下記の[参考資料]をご覧ください
コンパイル方法
プログラミング言語ごとにコマンドが異なりますので、ご注意ください。
$module load BaseCPU
$ mpiifort [options] source_file (Fortranの場合)
$ mpiicc [options] source_file (Cの場合)
$ mpiicpc [options] source_file (C++の場合)
利用可能なoptionについては、以下のページをご覧ください。
実行スクリプト
MPIを実行する際のコマンドは下記の通りです。
mpirun ${NQSV_MPIOPTS} -np (総並列数) 実行ファイル名
スクリプト例は下記の通りです。
4ノード並列実行、1ノードにつき76並列実行(計304並列実行)、経過時間 1時間でMPIのバッチリクエストを実行するスクリプトになります。
1 2 3 4 5 6 7 8 9 10 11 |
#!/bin/bash #PBS -q SQUID #PBS --group=【グループ名】 #PBS -l elapstim_req=1:00:00 #PBS -T intmpi #PBS -b 4 cd $PBS_O_WORKDIR module load BaseCPU mpirun ${NQSV_MPIOPTS} -np 304 ./a.out # 総並列数を-npの後に指定してください。 # 総並列数 = 利用するノード数(#PBS -b)×1ノードあたりの並列数 |
マルチノード実行時の注意点
-
ジョブスクリプトで「setenv (オプション名)」などでオプションや環境変数を指定した場合、マスターノードにのみ設定され、スレーブノードには設定されません。
全ノードに設定を反映させたい場合は「#PBS -v (オプション名)」と指定してください。
詳細は下記に記載しております。
ジョブスクリプトの書き方 環境変数の指定