Dentoo.LT12 並列処理・MPIの第一歩 20151025
-
Upload
sei-ichi-tanabe-tanabu -
Category
Technology
-
view
565 -
download
0
Transcript of Dentoo.LT12 並列処理・MPIの第一歩 20151025
並列処理・ MPI の第一歩Introduction to
Message Passing Interface
25 October 2015 / 於 : 電気通信大学 田名部 誠一 , Sei-Ichi Tanabe-Tanabu
※ 本資料内容および本自己紹介は所属組織の統一的見解ではなく、個人的見解によるものです。
@n_scattering .
seiichi.tanabetanabu .
Dentoo.LT #12 Lightning Talk
Google+もやっております。
2
こんにちは!
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
3
自己紹介
田名部 誠一 ( たなべ / たなぶ せいいち )• 所属 :– とある柳橋の計算機屋の末端社員
• 役割 :– ハイパフォーマンスコンピューティング (HPC) に
有用な (?) 技術の調査、プログラムの開発、社内情報システム管理など、極端に広く浅く
• 電通大との関わり :– 1994 年は落ち、 1995 年に電子物性工学科 (F 科 ) に
入学修士課程まで、渡辺信一研究室にいました。
– 別の大学で D を取ったが、研究者になりそこねた。2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
4
趣味 (?) にしていること
頻度が高めな順に
• Ingress ( 前回 #11 でお話ししました。 )– http://www.slideshare.net/seiichitanabetanabu/dentoolt11-20150510-lt
–運動療法として始めたが、歩き過ぎに注意• 各種 IT 勉強会• 発達障害 ( 自閉症スペクトラム ) ・双極性障害の
当事者会への参加• 乗り鉄 ( なかなかできない )• 研究所・研究施設等の一般公開の見学2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
5
なぜ HPC か、並列処理か?
• 並列計算の経験がない!これはまずい!• 計算機周辺の性能の向上
• CPU 、メモリ、ストレージ、ネットワーク…
• だけど、ユーザーの要求も高い!• そこで、並列計算! ( 計算機の共同作業! )–計算時間の短縮• N並列ならば、 1/Nの計算時間になるのが理想
だが、そうはいかないのが実情… ( チューニングなどの工夫 )
–大容量のメモリを必要とする計算も可能• 1 台でできないけど、クラスタにすればできる
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
6
並列計算機について
• 並列ではない計算機 ( 昔の PC)• 共有メモリ型並列計算機 ( 最近の PC も )
• 1 筐体にプロセッサ■ ( コア ) が複数• 全プロセッサが 1 つのメモリ■を共有• 並列化の手段 : MPI 、 OpenMP
• 分散メモリ型並列計算機 ( クラスタ構成 )• 筐体 (ノード ) が複数。ネットワーク接続• 各筐体が独自にメモリ■を所有• 並列化の手段 : MPI• →ノード間で通信をすることで、
全ノードのメモリが使えるようになる
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
メモリ
7
MPI とは?
• Message Passing Interface分散メモリ間のメッセージ通信の規格
• 実装としては MPICH 、 OpenMPI が有名https://www.mpich.org/http://www.open-mpi.org/
• プログラマが細かなチューニングを行える• 明示的に手続きを記述する必要がある
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
8
MPI 関数の紹介
• MPI 関数は数百種類。必要最低限の関数• 1. システム関数– MPI_Init; MPI_Comm_rank; MPI_Comm_size;
MPI_Finalize;• 2. 1対 1 通信関数 (多対多の通信もあるが割愛 )– MPI_Send; MPI_Recv;
• 3-1. 通信の同期– MPI_Barrier
• 3-2. 時間計測関数– MPI_Wtime
2015/10/25 /30Dentoo.LT #12 @ 電気通信大学
9
A. MPI_INIT
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• MPI環境の初期化。• すべての MPIルーチンの最初に 1 回だけ
必ずコールする必要がある。• CALL MPI_INIT(ierr)– ierr: 完了コードが戻る
10
B. MPI_FINALIZE
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• MPI環境の終了処理。• すべての MPIルーチンの最後に 1 回だけ
必ずコールする必要がある。• CALL MPI_FINALIZE(ierr)– ierr: 完了コードが戻る
11
C. MPI_COMM_RANK
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• コミュニケーター comm で指定したグループ (職場 ) 内での自分 (= コールしたプロセス ) のランク (「名前・社員番号」 ) を取得する。
• CALL MPI_COMM_RANK(comm,rank,ierr)– comm: コミュニケーター (職場 ) を指定( ここでは MPI_COMM_WORLD 、全体を指定します )
– rank: comm で指定したグループ内での自分 (= コールしたプロセス ) のランク ( 名前・社員番号 )
– ierr: 完了コードが戻る
12
D. MPI_COMM_SIZE
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• コミュニケーター comm で指定したグループ (職場 ) に含まれるプロセスの数を得る。
( プロセッサ数、並列数、「従業員数」 )• CALL MPI_COMM_SIZE(comm,procs,ierr)– comm: コミュニケーター (職場 ) を指定( ここでは MPI_COMM_WORLD 、全体を指定します )
– procs: comm で指定したグループ内に含まれるプロセスの数 (「従業員数」 )
– ierr: 完了コードが戻る
13
並列版 Hello プログラムの説明 (1)( プログラム 1, Fortran) 軽く
program main include "mpif.h" common /mpienv/myid,numprocs
integer myid, numprocs integer ierr
call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)
print *, "Hello parallel world! Myid:", myid
call MPI_FINALIZE(ierr)
stop end
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
14
並列版 Hello プログラムの説明 (2) ( プログラム 1, Fortran) 軽く
program main include "mpif.h" common /mpienv/myid,numprocs
integer myid, numprocs integer ierr
call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)
print *, "Hello parallel world! Myid:", myid
call MPI_FINALIZE(ierr)
stop end
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
このプログラムは、全プロセス( 全従業員 ) で起動される
A. MPI の初期化
C. 自プロセスの ID番号「社員番号」(myid) を取得します。
( 各プロセス・従業員で番号が異なります。 )
D. 全体のプロセッサ台数「従業員数」(numprocs) を取得します。
( 各プロセス・従業員で値は同じ )B. MPI の終了
15
並列版 Hello プログラムの説明(C言語、参考 ) 軽く
#include <stdio.h>#include "mpi.h"
int main(int argc, char* argv[]) {
int myid, numprocs; int ierr, rc;
ierr = MPI_Init(&argc, &argv); ierr = MPI_Comm_rank(MPI_COMM_WORLD, &myid); ierr = MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
printf("Hello parallel world! Myid:%d \n", myid);
rc = MPI_Finalize();
}
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
C言語では、関数名 (MPI_Init など ) の大文字・小文字の使い方が異なります
C言語では、完了コード (ierr) を得る方法が異なります
16
並列版 Hello 実行結果 軽く
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
[tanabe@tanabe sample]$ mpif90 sample1.f90 -o sample1( コンパイル )
[tanabe@tanabe sample]$ mpirun -n 2 sample1(2 並列「従業員数 2 名」で実行 )
Hello parallel world! Myid: 0 Hello parallel world! Myid: 1[tanabe@tanabe sample]$
( 全プロセス「従業員」に対して Hello parallel world!と、ランク「名前・社員番号」を表示する。同じ処理を実行しているが、 Myid の値で結果が変わる。 )
17
E. MPI_SEND
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• 1対 1ブロッキング通信サブルーチン (送信 )
• 送信メッセージを宛先プロセスに送信。–特定の従業員に「仕事を指示する」作業
• ただし、その人が聞いているとは限らない。→指示した従業員に案件番号を指定し指示を聞いてもらう作業が必要
• 送信したメッセージは MPI_RECV で受信する。
Myid=0
Myid=1
Myid=2
MPI_SEND!!
MPI_RECVで受信 !!
メッセージ
18
E. MPI_SEND 軽く
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• CALL MPI_SEND(buf,count,datatype, dest,tag,comm,ierr)– buf: 送信バッファーの先頭アドレス (変数 )– count: 送信メッセージの要素数 (※バイト数ではない )
– datatype: 送信メッセージのデータ型– dest: 宛先プロセスの comm内のランク ( 名前・社員番号 )
– tag: 送信メッセージの種類を区別するタグメッセージの「案件番号」
– comm: コミュニケーター「職場」を指定(ここでは MPI_COMM_WORLD 、全体を指定します )
– ierr: 完了コードが戻る
19
F. MPI_RECV
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• 1対 1ブロッキング通信サブルーチン (受信 )
• 送信元プロセスから送信されたメッセージを受信する。–特定の誰かから「仕事の依頼を受ける」作業
• ただし、その人への指示、受けるべき依頼とは限らない。→受けるべき特定の誰かからの指示、案件内容が必要
• MPI_SEND で送信したメッセージを受信する。
Myid=0
Myid=1
Myid=2
MPI_SENDで送信した !!MPI_RECVで受信 !!
メッセージ
20
F. MPI_RECV 軽く
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• CALL MPI_RECV(buf,count,datatype, source,tag,comm,status,ierr)– buf: 受信バッファーの先頭アドレス (変数 )– count: 受信バッファーの要素数 (※バイト数ではない )
– datatype: 受信メッセージのデータ型– source: 送信するプロセスの comm 内のランク (名前・社員番
号 )
– tag: 送信メッセージの種類を区別するタグ「案件番号」
– comm: コミュニケーター「職場」を指定( ここでは MPI_COMM_WORLD 、全体を指定します )
– status: 受信状況に関する情報 (整数配列 )– ierr: 完了コードが戻る
21
並列版送受信プログラムの説明 (1)( プログラム 2, Fortran) 軽く
program main include “mpif.h” common /mpienv/myid,numprocs
integer myid, numprocs integer isbuf, irbuf, ierr integer istatus(MPI_STATUS_SIZE)
call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)
if (myid.eq.0) then isbuf = 19750617 endif
(続く )
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
myid(ランク・社員番号 )が 0のプロセスの変数 isbufの変数を代入
MPI_RECVの処理で必要な変数受信状況に関する情報 (整数配列 )
準
備
22
並列版送受信プログラムの説明 (2)( プログラム 2, Fortran) 軽く
(続く ) if (myid.eq.0) then call MPI_SEND (isbuf,1,MPI_INTEGER,1,1,MPI_COMM_WORLD,ierr)
elseif (myid.eq.1) then call MPI_RECV (irbuf,1,MPI_INTEGER,0,1,MPI_COMM_WORLD, istatus, ierr)
endif
if (myid.eq.1) then write(*,*) "IRBUF =",irbuf endif
call MPI_FINALIZE(ierr)
stop end2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
Myid(ランク・社員番号 )が 1のプロセスの変数 irbufの内容を確認する。Myid=0のプロセス (従業員 )から、Myid=1のプロセス (従業員 )にデータが送信されている!
0から1に送信 !!
0から来たメッセージを1が受信 !!
処理の決定「案件番号」は 1
23
並列版送受信実行結果 軽く
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
[tanabe@tanabe sample]$ mpif90 sample2.f90 -o sample2( コンパイル )[tanabe@tanabe sample]$ mpirun -n 2 sample2(2 並列、従業員数 2 名で実行 )IRBUF = 19750617(従業員 Myid=0 から従業員 Myid=1 に対して、送信された整数値を受信できたことを確認する。 )
Myid=0
Myid=1
MPI_SENDで送信した !!
MPI_RECVで受信 !!
19750617 確認
24
G. MPI_BARRIER
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• コミュニケーター comm「職場」内の全プロセス「全従業員」間で同期をとる関数–ある職場の共同作業で、全部の結果が揃わな
いと次の作業ができない場合に、最後まで待つ
• CALL MPI_BARRIER(comm, ierr)– comm: コミュニケーター「職場」を指定( ここでは MPI_COMM_WORLD 、全体を指定します )
– ierr: 完了コードが戻る
25
H. MPI_WTIME
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• 時刻を得る関数。 (※ たぶん Unix 時間 )
• 経過時間の測定をしたい部分の前後で実行し、得られた値 elp の差を取ると、その部分の実行時間が得られる。
• elp = MPI_WTIME ()– elp: ある過去の時点からの経過時間 (秒 )
26
時刻同期プログラムの説明 (1)( プログラム 3, Fortran) 軽く
program main implicit none include "mpif.h" common /mpienv/myid,numprocs
integer myid, numprocs integer ierr integer*8 i, j, iinput, ioutput real*8 elp1, elp2
call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)(続く )
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
計算で必要な変数時間計測で必要な変数
心
の
準
備
27
時刻同期プログラムの説明 (2)( プログラム 3, Fortran) 軽く
(承前 )! preparation elp1 = MPI_WTIME() iinput=0 do i=1, 50000*(myid+1) do j=1, 50000 iinput = iinput + 1 enddo enddo
! mpi_barrier call MPI_BARRIER(MPI_COMM_WORLD, ierr)
! summation ioutput=0 do i=1, 50000 do j=1, 50000 ioutput = ioutput + 1 enddo enddo write(*,*) iinput, ioutput, 'myid=' , myid (続く )
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
(作業 1) Myid(ランク )の値に応じた回数だけ足し算を行う。(従業員により、かかる時間が異なる )
(作業 2) 決まった回数だけ足し算を行う。(従業員間でかかる時間はあまり変わらない )
結果の確認
全プロセスの同期を取る(全従業員の作業が終わるまで待つ )
時間の測定 (計算前 )
作業1
作業2
28
時刻同期プログラムの説明 (3)( プログラム 3, Fortran)
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• MPI_BARRIER で同期
• 同期しない場合
Myid=0
Myid=1
Myid=2
MPI_BARRIERで全プロセスの同期を取る(全従業員の作業が終わるまで待つ )
作業 1
作業 2経過時間
Myid=0
Myid=1
Myid=2
作業 2を行うためには、作業 1を終わらせなくてはいけない!そのために、同期が必要なはず。意図した動作が行われない!!
29
(承前 )
elp2 = MPI_WTIME()
write(*,*) 'ELAPSE=', elp2-elp1, 'myid=',myid call MPI_FINALIZE(ierr)
stop end
時間の測定 (計算後 )
時刻同期プログラムの説明 (4)( プログラム 3, Fortran) 軽く
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
計算前後時間の差を取る (経過時間 )
30
並列版時刻同期実行結果 軽く
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
[tanabe@tanabe sample]$ mpif90 sample3.f90 –o sample3( コンパイル )
[tanabe@tanabe sample]$ mpirun –n 2 sample3(2 並列、「従業員数 2 名」で実行 ) 5000000000 2500000000 myid= 1 ELAPSE= 31.489058017730713 myid= 1 2500000000 2500000000 myid= 0 ELAPSE= 33.371865987777710 myid= 0
MPI_BARRIER をしなかった場合 2500000000 2500000000 myid= 0 ELAPSE= 19.314102172851563 myid= 0 5000000000 2500000000 myid= 1 ELAPSE= 28.772753953933716 myid= 1
同期がとれていて、プロセス間で経過時間の違いは (そんなに )ない。
同期がとれていないので、プロセスごとに経過時間がバラバラ。
31
最後に
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• HPC 、大規模計算のためには並列化、特にメモリ分散型計算機 ( クラスター ) を使います。
• メモリ分散型計算機で並列計算を行うためには MPI による実装が (今のところ ) 必須です。
• MPI のサブルーチンの数は多いが、少数の基礎的なもので入門可能です。
• この業界にいながら、職種の関係でクラスター構築の経験がほぼないのが悩み。
32
参考となりそうな文献など
2015/10/25 /32Dentoo.LT #12 @ 電気通信大学
• スパコンプログラミング入門 : 並列処理と MPI の学習 (片桐 孝洋 著、東京大学出版会 )※他にも、 OpenMP などに着目した著書などもあり
• 並列プログラミング虎の巻MPI版(青山 幸也 著、高度情報科学技術研究機構 )※OpenMP 、チューニングのテキストもありhttp://www.hpci-office.jp/pages/seminar_text
• MPICH サイトhttps://www.mpich.org/
• OpenMPI サイトhttp://www.open-mpi.org/ などなど
• サンプルプログラムは、 Github にありますhttps://github.com/sittncs/hpcstudy/