並列計算の基礎 MPI を用いた並列計算
並列計算の環境 並列計算 複数の計算ユニット(PU, ore, Pなど を使用して 一つの問題 計算 を行わせる 近年 並列計算を手軽に使用できる環境が急速に整いつつある >通常のP PU(entral Processing Unit)上に計算装置であるoreが 複数含まれている Intel ore i7 シリーズ: 4つの計算装置(ore)
通常のプログラム ( 非並列 ) 通常のプログラミングは非並列 一つの問題を一つの計算装置 (ore) で計算 計算装置 (4 ores) 計算 : F = 400 i i=1 ore 0 ore 1 ore 2 ore 3 時間 F = 400 i i=1 i=1 400 の計算 何もしていない
並列プログラム 一つの問題を一つの計算装置 (ore) で計算 ore0 : i=1 100, ore1: 101 200, ore2: 201 300, ore3: 301 400 F 0 = 100 i i=1 F 1 = 最後に, F = F 0 + F 1 + F 2 + F 3 200 300 i F 2 = i i=101 i=201 ore 0 ore 1 ore 2 ore 3 400 F 3 = i i=301 4ores を使用した場合 使用した ore 数 ( 並列数 ) に比例して計算を高速化 非並列計算の時間 F 0 = 100 i 400 i=1 i F = 200 F 1 = 合算 i=1 i i=101 300 i=1 400 F = F 0 + F 1 + F 2 + F 3 F 2 の計算 = i i=201 F 1 F 2 データを転送 F 3 F 3 = 400 i i=301 データ転送など並列計算に伴う, 計算以外の処理に時間が必要 並列数に比例することはない
並列プログラムの実現方法 言語,FORTRN 言語で並列計算を実現するライブラリを使用して拡張する. ライブラリ 付加的な幾つかの関数等を予め用意したパッケージ 代表的な並列プログラミング環境 OpenMP 非並列プログラム中に幾つかの文を書き加えるのみ. ネットワークで結合された並列計算は出来ない. (1 台の P, コンピュータ上でのみ OK) MPI (Message Passing Interface) PVM (Parallel Virtual Machine) ネットワーク結合環境でも OK
ネットワーク結合環境 計算機クラスター, P クラスター 複数の計算機,P( ノード ) をネットワークでつなげる TSUME2.0 1407 6 9,000ores 1407 ノード PU0 Xeon X5670 12ores PU1 Node 0 Node 1 Node 2 Node 3 Node 4 Infini and(gb Ether でも OK)
MPI (Message Passing Interface) ライブラリ 並列計算に必要な関数の種類と仕様を規定したもの ( 設計書 ) 現在の最新仕様 :MPI 2.0 実際のライブラリは, ベンダーや各種団体にて実装されている. 無料のライブラリが幾つか開発されており, 定評がある. 代表 Open MPI : hwp://www.open-mpi.org MPIH : hwp://www.mpich.org 何れのライブラリを使用しても, 関数は MPI 仕様書に従った形をしており, MPI を使用したプログラムは何れのライブラリをインストールしているかに関係無く実行可能となる.
MPI で使用する代表的な関数 基本的なプログラミングは 6 関数で実現可能! MPI 計算の管理に関するもの MPI_INIT MPI_FINLIZE MPI_OMM_SIZE MPI_OMM_RNK データの通信 ( 受け渡し ) に関するもの MPI_SEND MPI_REV
プログラミングの実際 基本スタンス :SPMD (Single Program, Mulaple Data) 同一のプログラムが異なる計算装置 (ore) 上で複数実行される. ore0 上の は別の場所にあるので,ore1 から直接参照することは出来ない (MPI_REV, MPI_SEND などの通信関数を使用する ) ore0 ore1 ore2 ore3 RNK=0 RNK=1 RNK=2 RNK=3 通信で持ってくる それぞれが, N のデータを持つ. 読込んで実行 REL*8 INTEGER N.. MPI で書かれたプログラム 各プログラムは RNK と呼ばれる番号 (0,1,2,3, ) が付けられ 他と区別される
プログラム例 処理 : F=1/2+1/3+1/4 を行い, 出力するプログラム REL*8,, REL*8 F 非並列 =1.0/2.0 =1.0/3.0 =1.0/4.0 F=++ WRITE(*,*) F END
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) rank=0 rank=1 rank=2 =1/2 =1/3 =1/4 IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) =1/2 を転送 を転送 IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ F=++ WRITE(*,*) F WRITE F LL MPI_FINLIZE(ierr) END
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ WRITE(*,*) F LL MPI_FINLIZE(ierr) END MPI の関数を使用する場合に必要なおまじない. (MPI_... 関数の定義が mpif.h というファイルに書かれている )
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ WRITE(*,*) F LL MPI_FINLIZE(ierr) END MPI の関数を使用する場合に必要なおまじない. ( 他の MPI_... 関数を使用する前に, 必ず書くこと ) 書式 MPI_INIT( integer ierr) ierr : Integer 型の変数, 正しく実行されたのかを判定する値が代入される.
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F total_process=3 LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ WRITE(*,*) F LL MPI_FINLIZE(ierr) END 並列計算のプロセス (rank) の総数を取得 ( プログラムの起動時に使用するプロセスを変更できる ) MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) MPI_OMM_WORLD : おまじない INTEGER total_process : ランクの総数 ( 例では 3 が代入 ) INTEGER ierr : 正しく実行できたかのフラグ
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F total_process=3 rank=0 rank=1 rank=2 LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ WRITE(*,*) F LL MPI_FINLIZE(ierr) END プロセス自身のrank 数を取得 (0 total_process-1 番まで個々のプロセスに番号がつく ) MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) MPI_OMM_WORLD : おまじない INTEGER rank : 自身のランク数が代入される INTEGER ierr : 正しく実行できたかのフラグ
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) rank=0 rank=1 rank=2 =1/2 =1/3 =1/4 IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) Rank=0: の計算 LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ WRITE(*,*) F LL MPI_FINLIZE(ierr) END 自身のランク (rank) 数に応じて, 動作を切り替える. Rank=1: の計算 Rank=2: の計算
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) rank=0 rank=1 rank=2 =1/2 =1/3 =1/4 IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) を rank=1 から rank=0 へ転送 LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) =1/2 を転送 を転送 IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) をrank=2からrank=0へ転送 LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) F=++ WRITE(*,*) F LL MPI_FINLIZE(ierr) END
を rank=1 から rank=0 へ転送 rank=0 rank=1 MPI でのデータの転送の基本 : 送信 (Send) & 受信 (Recv) の組み合わせ 送信側 (rank=1) では, 宛先 (rank=0) と通信を区別する タグ を付けて相手に送る. =1/3 rank=0 に向けて tag=10 の通信 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) MPI_SEND( SentValue, SizeOfValue, MPI_DT_TYPE, Dest, Tag, MPI_OMM_WORLD, ierr) SentValue : 送信される変数 ( ) SizeOfValue : 送信する変数の大きさ ( 今は 1 ) MPI_DT_TYPE : 送信する変数の型 (MPI_DOULE_PREISION, MPI_INTEGER, ) Dest : 送信先の rank 数 Tag : タグ ( 任意の整数 ) MPI_OMM_WORLD : おまじない ierr : エラー識別用
を rank=1 から rank=0 へ転送 rank=0 rank=1 MPIでのデータの転送の基本 : 送信 (Send) & 受信 (Recv) の組み合わせ rank=1からの, tag=10のみ受信受信側 (rank=0) では, 送信元 (rank=1) と通信を区別する タグ によって受信するメッセージを選択. =1/3 rank=0 に向けて tag=10 の通信 LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) MPI_REV( RecvValue, SizeOfValue, MPI_DT_TYPE, Dept, Tag, MPI_OMM_WORLD, istat, ierr) RecvValue : 受信結果を格納する変数 ( ) SizeOfValue : 格納する変数の大きさ ( 今は 1 ) MPI_DT_TYPE : 変数の型 (MPI_DOULE_PREISION, MPI_INTEGER, ) Dept : 送信元の rank 数 Tag : タグ ( 任意の整数 ) MPI_OMM_WORLD : おまじない istat : 受信が完了したかなどの情報 ( おまじない ) ierr : エラー識別用
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) rank=0 rank=1 rank=2 =1/2 =1/3 =1/4 IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) rank=0 のプロセスでのみ, IF(rank.eq. 2) THEN LL MPI_SEND(, F=++ 1, MPI_DOULE_PREISION, を計算して 0, 20,, MPI_OMM_WORLD, 出力する ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) =1/2 F=++ を転送 を転送 F=++ WRITE(*,*) F WRITE F LL MPI_FINLIZE(ierr) END
MPI を用いた並列プログラム INLUDE mpif.h INTEGER ierr INTEGER istat INTEGER rank INTEGER total_process REL*8,, REL*8 F LL MPI_INIT(ierr) LL MPI_OMM_SIZE(MPI_OMM_WORLD, total_process, ierr) LL MPI_OMM_RNK(MPI_OMM_WORLD, rank, ierr) rank=0 rank=1 rank=2 =1/2 =1/3 =1/4 IF(rank.eq. 0) =1.0/2.0 IF(rank.eq. 1) =1.0/3.0 IF(rank.eq. 2) =1.0/4.0 IF(rank.eq. 1) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 10, MPI_OMM_WORLD, ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 1, 10, MPI_OMM_WORLD, istat, ierr) MPI を使用した場合には, 必ず =1/2 を転送 を転送 IF(rank.eq. 2) THEN LL MPI_SEND(, 1, MPI_DOULE_PREISION, 0, 20, MPI_OMM_WORLD, ierr) MPI_FINLIZE(ierr) LL MPI_REV(, 1, MPI_DOULE_PREISION, 2, 20, MPI_OMM_WORLD, istat, ierr) を書くこと. F=++ F=++ WRITE(*,*) F WRITE F LL MPI_FINLIZE(ierr) END
MPI を使用したプログラムのコンパイル方法 gfortran や gcc に代えて, > mpif90 -o run sample.f > mpicc -o run sample.c などの用に, mpif90 や mpicc を用いる
MPI を使用したプログラムの実行方法 MPI を使用したプログラムの実行は, 以下のように行う. > mpirun -np 総ランク数 実行するプログラム 例えば run というプログラムを,3 個の総ランク数で実行する場合には, > mpirun -np 3 run となる
プログラムの実行に要した時間の測定方法 プログラムの実行に費やした時間は, 以下のように ame コマンドで計測できる > ame -p プログラム 結果は, real, user, sys の 3 種類が出力されるが, real の値が実際に経過した 秒数 である. 例えば run というプログラムを,3 個の総ランク数で実行する場合の経過秒を計測する場合には, > ame -p mpirun -np 3 run となる
TSUME の利用方法 TSUME へはネットワーク越しにログインして利用する. ターミナル を起動して, > ssh username@login-t2.g.gsic.atech.ac.jp と打ち込む (username は各自の教育システムへのログイン名 ) パスワー認証ののち,TSUME へログイン完了となる