reedbushスーパーコンピュータシステム の利用...
TRANSCRIPT
Reedbushスーパーコンピュータシステムの利用とMPIプログラムの実行
松本正晴大学院情報理工学系研究科コンピュータ科学専攻
計算科学概論(第2回):4月16日(月)
本日の講義資料
ITC-LMSの授業スライドにある「計算科学概論0409.pdf」(前回分の資料)と「計算科学概論0416.pdf」(今回の資料)を各自ダウンロードしてください。
https://itc-lms.ecc.u-tokyo.ac.jp/portal/login
前提とする知識,条件 C言語またはFortranによるプログラミングの経験は無くてもいいが、今日は上記の経験がある程度あることを前提に話を進めます。
UNIX環境についての基本的な知識と利用経験 UNIXの基本コマンド,及び,プログラムの基本がわかってないと厳しい 個別に,集中的に教えるよう配慮しますので,遠慮なく聞いてください
知ってる人は近くの人にどんどん教えてあげるとよい。知らない人同士で相談しながらやるのもよし。
情報基盤センター教育用計算機システム(ECCS)のアカウントを予め取得しておくこと
http://www.ecc.u-tokyo.ac.jp/doc/announce/newuser.html
今日はReedbushスーパーコンピュータシステムの使い方、ならびにMPIの基礎についての実習です。OpenMPやGPUを使った計算は扱いません。
Reedbushアカウントの発行
1. 本人証明ができるものを用意。
• 学生証、運転免許証、など
• 無い場合は、ECCSにログインできることで証明。
2. 名前を呼びます。
3. 本人確認の上、アカウントが記載された紙を
配布します。
4. 授業で指示があるまで、ログイン作業を行わ
ないでください。
(先週までに名簿登録した者のみです。)
ECCSにログイン
1. ECCSのマシンにログインする
• ユーザ名、パスワードを間違えずに!
• 今配った<スパコンのアカウント>ではない。
2. デスクトップ画面下の「ターミナル」をクリックする。
3. コンソール画面が表示される。
(以降、macでの説明です。)
ユーザ名の確認
ユーザ名(利用者番号):t03xxx (xxxは3桁番号)
グループ名(課金プロジェクト名):gt03
紙に書いてある情報が間違えている場合は教えてください。
秘密鍵/公開鍵の作成
1. ターミナルを起動して、以下を入力する$ ssh-keygen -t rsa
2. 鍵の収納先を聞かれるので、リターンを押す(以前に別の鍵を作成してそれを使っている場合は、ファイル名を変更しないと上書きしてしまうので注意)
3. 鍵を使うためのパスワードを聞かれるので、自分の好きなパスワードを入れる(パスフレーズと呼ぶ)
4. もう一度、上記のパスフレーズを入れる5. 鍵が生成される。
鍵の利用(1/2)
1. 生成した鍵は以下に入っている.ssh/
2. 以下を入力する$ cd .ssh/
3. 以下を入力すると、ファイルが見える(以下は例)$ ls
id_rsa id_rsa.pub known_hosts
• 以下が生成した鍵ファイルid_rsa :秘密鍵id_rsa.pub :公開鍵
鍵の利用(2/2)
4. 以下を入力して、公開鍵を表示する$ cat id_rsa.pub
<公開鍵が表示される>
5. “ssh-rsa…”で始まる部分を、マウスでカットアンドペーストし、公開鍵の登録に使う(“.u-tokyo.ac.jp”まで)
公開鍵の登録(1/2)
1. Webブラウザ(Safari)を起動
2. 以下のポータルサイトのアドレスを入力するhttps://reedbush-www.cc.u-tokyo.ac.jp/
3. 「ユーザ名」にセンターから配布された“利用者番号”を入力する4. 「パスワード」に、センターから配布された“初期パスワード”を入力する記載されている文字列はパスワードではないので注意【次ページを参照】
5. “Login”ボタンを押すと、“初期パスワード”を変更するよう指示されるので、新しいパスワードを入力して変更する鍵を作成した際のパスフレーズとは別の文字列を使うこと
正しいパスワードの確認とログイン
表示されている文字列の奇数番号(1, 3, 5, 7, 9,
11, 13, 15)の8文字列をつなぎ合わせたもの
例: P9aesWsbw6oZrrd5
→ Password
ポータル画面(ログイン前)
利用者番号とパスワードを入力
公開鍵の登録(2/2)
5. パスワードの変更が完了したら、利用者番号と新しいパスワードを入力して、“Login”ボタンを押す
6. ログインメッセージが出る7. (“Change Language”メニューで言語を変更)8. 左側メニューの「公開鍵アップロード」をクリック9. 画面に、公開鍵をカットアンドペーストする10.「作成」ボタンを押す
言語の変更“Change Language”で日本語に変更できます
• “Japanese (JA_JP.UTF-8”)を選んで“Change”を押す• 終わったら、ブラウザを再読み込み
1.ここをクリック
2.ここを選択3.ここをクリック
ポータル画面(日本語選択後)
ここをクリック
ポータル画面(公開鍵登録)
1.ここに公開鍵をペースト
2.ペースト後、クリック
ポータル画面(公開鍵登録成功)
ペーストした鍵が表示されていればOK
Reedbushへログイン
ターミナルから、以下を入力$ ssh reedbush-u.cc.u-tokyo.ac.jp -l t03xxx
「-l」はハイフンと小文字のL
「t03xxx」は利用者番号(t+数字)
接続するかと聞かれるので、yesを入力する 鍵作成時に自分で決めたパスフレーズを入力する 成功するとログインできる
鍵の名前がid_rsaじゃない場合や、.ssh/に無い場合はオプション「-i」で鍵を指定できる
ログイン時は必ず/home/gt03/t03XXX/に入る。
ReedbushのデータをローカルPCに取り込む
ローカルPC(ECCS)のターミナルでscpコマンドを使う。
$ scp [email protected]:~/a.f90 ./
• 「t03xxx」は利用者番号• 上の例はReedbush上のホームディレクトリにあるa.f90を
ローカルPCのカレントディレクトリに取ってくる• ディレクトリごとダウンロードする場合は-rを指定
$ scp –r [email protected]:~/SAMPLE ./
• SAMPLEディレクトリをディレクトリごとダウンロード
ローカルPCのファイルをReedbushに置く
同様にローカルPC(ECCS)のターミナルでscpコマンドを使う。
$ scp ./a.f90 [email protected]:
• 「t03xxx」は利用者番号• 上の例はローカルPCのカレントディレクトリにあるa.f90を
Reedbush上のホームディレクトリに置く• ディレクトリごとアップロードする場合は-rを指定
$ scp –r ./SAMPLE [email protected]:
• SAMPLEディレクトリをディレクトリごとアップロード
GUIによるファイル操作(主にWindowsユーザ向け)
FileZillaやWinSCPを使えば手元のパソコンとReedbush間のファイル転送をGUI操作で行うことができる
FileZilla• https://filezilla-project.org
• “Download Filezilla Client”からダウンロード• サイトマネージャにてプロトコルをSFTPに設定、ログオンの種類を鍵ファイルにする(Putty形式の公開鍵ファイルが必要、puttygenによって変換すると良い)
WinSCP• https://winscp.net/eng/download.php
• プロトコルをSFTPまたはSCPに設定する• ホスト設定画面の設定からSSH-認証を選び、秘密鍵を指定する(OpenSSH形式・Putty形式の両方に対応)
異なるパソコンの公開鍵登録
1. 同様の手順により、異なるパソコンで公開鍵を作成
2. 「公開鍵のアップロード」メニューの中で、複数登録することが可能
3. 鍵認証なので、家のパソコンや自分のノートパソコンからでも接続可能です。
Reedbushにおける注意点
• ログイン時に始めに入る/homeファイルシステムは容量が小さく(最大2GB)、ログインに必要な設定ファイルだけを置くための場所です。
• /homeに置いたファイルは計算ノードから参照できません。ジョブの実行もできません。
⇒ ログイン後は/lustreファイルシステムを利用してください。
• ホームディレクトリ:/home/gt03/t03xxx• cdコマンドで移動できます。
• Lustreディレクトリ:/lustre/gt03/t03XXX• cdwコマンドで移動できます。
Reedbushにおける計算の仕方
Reedbush-U( CPU only )
Reedbush-H( w/ GPU )(今回の講義ではGPUは扱いません)
Reedbush-U 1ノードの構成
メモリ128GB
Intel Xeon E5-2695
v4 (Broadwell-EP)
18コア
DDR4
DDR4
DDR4
DDR4
QP
I
QP
I
メモリ128GB
Intel Xeon E5-2695
v4 (Broadwell-EP)
18コア
DDR4
DDR4
DDR4
DDR4
76.8GB/s76.8GB/s76.8GB/s
1ノード当たり36コア、256GB
×420ノード
G3 x16
Infin
iband
ED
R H
CA
一般的なスーパーコンピュータでは、ノード単位で計算資源を数えることが多い。
Reedbush-Uの場合、1ノード当たり36コア、256GB(18コア、128GBが2つ)。これが全部で420
ノードある。
ノード内は共有メモリ型計算機なので、OpenMPによる並列化が可能。
ノード間で並列化する際はMPIによるデータ通信が必要(分散メモリ型計算機)。
(ちなみに)Reedbush-Hノードのブロック図
26
NVIDIAPascal
NVIDIAPascal
NVLinK
20 GB/s
IntelXeonE5-2695v4(Broadwell-
EP)
NVLinK
20 GB/s
QPI76.8GB/s
76.8GB/s
IBFDRHCA
G3x16
15.7 GB/s 15.7 GB/s
DDR4
128GB
EDRswitch
EDR
76.8GB/s 76.8GB/s
IntelXeonE5-2695v4
(Broadwell-EP)QPI
DDR4DDR4DDR4
DDR4DDR4DDR4DDR4
128GB
PCIe sw
G3
x16
PCIe sw
G3
x16
G3
x16
IBFDRHCA
×120
Reedbush-Uで利用できるコンパイラ
Intelコンパイラ• Fortranコンパイラ :ifort (逐次実行)
:mpiifort (MPI実行,Intel MPI)• Cコンパイラ :icc (逐次実行)
:mpiicc (MPI実行,Intel MPI)• C++コンパイラ :icpc (逐次実行)
:mpiicpc (MPI実行,Intel MPI)
ソースファイルをコンパイルして実行ファイルを作成した後にそれを実行するには“ジョブ”を投げる必要がある。
Reedbush-Hで利用できるGPU用コンパイラ(PGI)
• moduleコマンドを利用して、コンパイラ環境、パスを設定。$ module load cuda/(バージョン番号)$ module load pgi/(バージョン番号)
• CUDA C$ nvcc –gencode arch=compute_60,code=sm_60 [options] hoge.cu
• CUDA Fortran$ pgfortran –Mcuda=cc60 [options] hoge.cuf
• OpenACC• Fortran: $ pgfortran –acc –ta=tesla,cc60 [options] hoge.f90
• C: $ pgcc –acc –ta=tesla,cc60 [options] hoge.c
• C++: $ pgc++ -acc –ta=tesla,cc60 [options] hoge.cpp
Reedbush-Uスーパーコンピュータシステムでのジョブ実行形態
インタラクティブジョブ実行(本実習では利用不可)• PCでの実行のように、コマンドを入力して実行• スパコン環境では、あまり一般的でない• あくまでもデバッグ用。大規模実行はできない• Reedbush-Uでは、以下に限定
• 1ノード(36コア)、30分まで• 4ノード(144コア)、10分まで
バッチジョブ実行• バッチジョブシステムに処理を依頼して実行する• スパコン環境では一般的な方法。大規模実行用。• 一般利用では、最大128ノード(4,608コア)、24時間まで。• 本実習では最大8ノード(288コア)、10分まで。
Reedbush-Uに限らず、スパコン環境では以下の2通りがほとんど。
バッチ処理とは?
ユーザスパコン
バッチ処理システムがジョブを取り出す
実行
バッチキュー
ジョブの依頼
• 一般的なスパコン環境で大規模計算を行う場合、通常、インタラクティブ実行(コマンドラインで実行すること)はできません
• ジョブはバッチ処理で実行します• キュー(Queue):待ち行列
バッチキューの設定の仕方
• バッチ処理はAltair社のバッチシステムPBS Professionalで管理されている
• 以下、主要なコマンドの説明• ジョブの投入 :qsub <ジョブスクリプトファイル名>
• 自分が投入したジョブの状況確認 :rbstat• 投入ジョブの削除 :qdel <ジョブID>
• バッチキューの状態を見る :rbstat --rsc
• バッチキューの詳細構成を見る :rbstat --rsc -x
• 投げられているジョブ数を見る :rbstat –b
• 過去の投入履歴を見る :rbstat –H
• 同時に投入できる数/実行できる数を見る :rbstat --limit
Jobスクリプトのサンプルの説明C言語,Fortran共通
#!/bin/bash
#PBS -q u-lecture
#PBS –W group_list=gt24
#PBS -l select=8:mpiprocs=4:ompthreads=9
#PBS -l walltime=00:10:00
cd $PBS_O_WORKDIR
. /etc/profile.d/modules.sh
mpirun ./a.out
リソースグループ名:u-lecture → 授業時間中はu-lecture3
利用グループ名:gt03
利用ノード数
1ノード当たりのMPIプロセス数
実行時間制限:10分
カレントディレクトリ設定、環境変数設定(必ず記入しておく)
上の例では、ジョブを32プロセス9スレッドの288並列(コア)で実行(1ノード当たり4プロセス9スレッドでそれが8ノード分)
1プロセス当たりOpenMP
スレッド数
Reedbush-Uは1ノード当たり36コア。
本講義でのキュー名
本演習時間中のキュー名:u-lecture3(Reedbush-U, CPU only)最大8ノード(288コア),10分までh-lecture3(Reedbush-H, w/GPU)最大2ノード、10分まで(CPU:36コア+GPU:2台)×2ノード
本演習時間以外(24時間)のキュー名: u-lecture(Reedbush-U)h-lecture(Reedbush-H)利用条件は上と同じ
ジョブスクリプトの#PBS -qの部分に記載するキューの種類
サンプルプログラムの起動
1.並列版Helloプログラムの実行
2.MPI集団通信に関するプログラムの実行
UNIXコマンド備忘録(1/3)
emacsエディタの起動: emacs 編集ファイル名• ^x ^s (^は「controlを押しながら」) :テキストの保存• ^x ^c :終了(^zで終了すると、スパコンの負荷が上がる。絶対にしないこと)
• ^g :訳がわからなくなったとき• ^k :カーソルより行末まで消す。
消した行は一時的に記憶される。• ^y :^kで消した行を、現在のカーソルの場所にコピー• ^s文字列 :文字列の箇所まで移動(検索)• ^M x goto-line :指定した行まで移動(^MはESCキーを押して離す)
UNIXコマンド備忘録(2/3)
• rm ファイル名 :ファイルを消す• ls :現在いるフォルダの中身を見る• cd ディレクトリ名 :ディレクトリに移動する
• cd .. :一つ上のディレクトリへ移動する• cd :ホームディレクトリへ行く
• cat ファイル名 :ファイルの中身を見る• make :Makefileがある場合、実行ファイルを作る
• make clean :実行ファイルを消す(cleanがMakefile内で定義されていないと実行できない。)
UNIXコマンド備忘録(3/3)
• less ファイル名 :ファイルの中身を見る(catでは画面がいっぱいになってしますとき)
• スペースキー :1画面スクロール• / 文字列 :文字列の箇所まで移動• q :終了(訳がわからなくなったとき)
サンプルプログラム名
• C言語版・Fortran版共通ファイル: Samples.tar
• tarで展開後、C言語とFortranのディレクトリが作られる。C/ :C言語用F/ :Fortran用
• 上記ファイルが置いてある場所/lustre/gt03/t03000
Samples.tarの中身
• Hello/
• 並列版Helloプログラム
• Mat-vec/
• 行列・ベクトル積の計算プログラム
• MPI/
• MPI集団通信のサンプルプログラム
• Wa1/
• 逐次転送方法による総和演算プログラム
• Wa2/
• 二分木通信方式による総和演算プログラム
並列版Helloプログラムをコンパイル(1/2)
1. cdwコマンドを実行してlustreファイルシステムへ移動$ cdw
2. サンプルプログラムの場所/lustre/gt03/t03000にある
Samples.tarを自分のディレクトリにコピーする$ cp /lustre/gt03/t03000/Samples.tar ./
3. Samples.tarを展開する$ tar xvf Samples.tar
4. Samplesディレクトリへ移動$ cd Samples
C言語 : $ cd CFortran : $ cd F
5. Helloディレクトリへ移動$ cd Hello
並列版Helloプログラムをコンパイル(2/2)
6. ソースファイルをコンパイルする。すでにcompileファイルという実行ファイルが用意されているので、それを実行する。$ ./compile
(以下のコマンドでもコンパイル可)C言語 :mpiicc hello.c –o hello
Fortran :mpiifort hello.f –o hello
7. 実行ファイル(hello)ができていることを確認する$ ls
並列版Helloプログラムの実行(ピュアMPI)
• このサンプルのJobスクリプトはhello.bash
です。• サンプルでは、キュー名が“u-lecture”,グループ名が
“gt00”になっています。• $ emacs hello.bash
で、キュー名とグループ名をそれぞれ書き換えてください。u-lecture3
gt03
並列版Helloプログラムの実行(ピュアMPI)
1. Helloディレクトリの中で以下を実行$ qsub hello.bash
2. 自分の導入されたジョブを確認$ rbstat
3. 実行が終了すると、以下のファイルが生成されるhello.bash.exxxxxx
hello.bash.oxxxxxx (xxxxxxはJobID)
4. 上記の標準出力ファイルを見てみるcat hello.bash.oxxxxxx
5. “Hello parallel world!”が36プロセス×8ノード
=288表示されていたら実行成功。
バッチジョブ実行時の標準出力と標準エラー出力
• バッチジョブの実行が終了すると、標準出力ファイルと標準エラー出力ファイルがジョブ投入時のディレクトリに作成される。
• 標準出力ファイルにはジョブ実行中の標準出力、標準エラー出力ファイルにはジョブ実行中のエラーメッセージが出力される。
ジョブ名.oXXXXX --- 標準出力ファイルジョブ名.eXXXXX --- 標準エラー出力ファイル(XXXXX はジョブ投入時に表示されるジョブのJobID)
並列版Helloプログラム(C言語)
#include <stdio.h>
#include <mpi.h>
int main(int argc, char *argv[]){
int myid,nprc,ierr;
ierr=MPI_Init(&argc,&argv);
ierr=MPI_Comm_rank(MPI_COMM_WORLD,&myid);
ierr=MPI_Comm_size(MPI_COMM_WORLD,&nprc);
printf("Hello parallel world! MyID: %d %d¥n",myid, nprc);
ierr=MPI_Finalize();
return 0;
}
MPIの初期化
自分のrank IDを取得:各プロセスで値は異なる
全体のプロセッサ台数を取得:各プロセスで値は同じ(演習環境では最大288)
MPIの終了
このプログラムは、全PEで起動される
program main
implicit none
include "mpif.h"
integer::myid,nprc,ierr
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world,myid,ierr)
call mpi_comm_size(mpi_comm_world,nprc,ierr)
print *, "Hello parallel world! MyID:", myid, nprc
call mpi_finalize(ierr)
stop
end program main
並列版Helloプログラム(Fortran)
MPIの初期化自分のrank IDを取得:各プロセスで値は異なる
全体のプロセッサ台数を取得:各プロセスで値は同じ(演習環境では最大288)
MPIの終了
このプログラムは、全PEで起動される
MPIの集団通信プログラム
前回の資料(計算科学概論0409.pdf)の85ページからを参照
プログラムをよく読んで、コンパイル・実行し、結果を確認してください。
サンプルプログラムの起動
3.行列・ベクトル積プログラムの実行
Samples.tarの中身
• Hello/
• 並列版Helloプログラム
• Mat-vec/
• 行列・ベクトル積の計算プログラム
• MPI/
• MPI集団通信のサンプルプログラム
• Wa1/
• 逐次転送方法による総和演算プログラム
• Wa2/
• 二分木通信方式による総和演算プログラム
行列・ベクトル積の計算プログラム(Mat-vec)
実行結果が、
N = 10000
Elapsed time = 0.286047 [sec.]
699.185843 [MFLOPS]
OK!
N = 10000
Elapsed time[sec.] = 1.46469116210938
MFLOPS = 136.547557037191
OK!
(C言語)
(Fortran)
のような結果が出たらOK
サンプルプログラムの説明
本プログラムでは、全プロセスが行列Aとベクトルxのデータを持ち、y=Axの計算をしている。
(C言語) #define N 10000
(Fortran) integer,parameter::N=10000
Nを変更すると、行列サイズを変更できます。
(C言語) #define debug 1
(Fortran) integer,parameter::debug=1
「1」としてコンパイルすると、演算結果が正しいことがチェックできます。
レポート課題(I)
下記について、実験環境(8ノード、288コア)を駆使して、問題サイズや並列数を変化させるなどにより性能を評価し、レポートにまとめよ。
1. サンプルプログラムを並列化せよ。このとき、行列Aおよびベクトルx、yのデータは、全PEでN×Nのサイズを確保してよい。
2. サンプルプログラムを並列化せよ。このとき、行列Aは、初期状態では、各PEに割り当てられた分の領域しか確保してはいけない。
レポート課題(I)の注意
本課題では、MPI通信関数は不要です。
このサンプルプログラムでは、演算結果検証部分(debug=1にした場合に実行される部分)が並列化されていないため、MatVec関数のみを並列化しても、検証部でエラーとなります。
検証部分も、計算されたデータに各PEで対応するように、並列化してください。検証部分においても、行列-ベクトル積と同様のループとなります。
本実習プログラムのTIPS
• myid, nprcは大域変数ですmyid (=自分のID)、および、nprc(=全プロセス数)の変数は大域変数です。MyMatVec関数内で、引数設定や宣言なしに、参照できます。
• myid, nprcの変数を使う必要がありますMyMatVec関数を並列化するには、myidおよび、nprc変数を利用しないと、並列化ができません。
並列化の考え方(C言語)
for ( j=0; j<N; j++) { 内積( j, i ) }
PE0
for ( j=0; j<N/4; j++) { 内積( j, i ) }
PE1
for ( j=N/4; j<(N/4)*2; j++) { 内積( j, i ) }
PE2
for ( j=(N/4)*2; j<(N/4)*3; j++) { 内積( j, i ) }
PE3
for ( j=(N/4)*3; j<N; j++) { 内積( j, i ) }
各PEで重複して所有する
行列A
ベクトルx
n
n
SIMDアルゴリズムの考え方(4PEの場合)
並列化の考え方(Fortran言語)
do j=1, N 内積( j, i )
enddo
PE0
do j=1, N/4内積( j, i )
enddo
PE1
do j=N/4+1, (N/4)*2内積( j, i )
enddo
PE2
do j=(N/4)*2+1, (N/4)*3内積( j, i )
enddo
PE3
do j=(N/4)*3+1, N内積( j, i )
enddo
各PEで重複して所有する
行列A
ベクトルx
n
n
SIMDアルゴリズムの考え方(4PEの場合)
PE0 PE3PE2PE1
初心者が注意すること
A[N][N] A[N][N] A[N][N] A[N][N]
PE0 PE3PE2PE1
myid = 0 myid = 1 myid = 2 myid = 3
各PEでは、独立した配列が個別に確保されます。
myid変数は、MPI_Comm_rank()関数が呼ばれた段階で、各PE固有の値になっています。
1. 全PEで行列AをN×Nの大きさ、ベクトルx、yをNの大きさ、確保してよいとする。
2. 各PEは、担当の範囲のみ計算するように、ループの開始値と終了値を変更する。ブロック分散方式では、以下になる。(N が nprcで割り切れる場合)
ib = N / nprc;for ( nj=myid*ib; nj<(myid+1)*ib; nj++) { … }
3. (2の並列化が完全に終了したら)各PEで担当のデータ部分しか行列を確保しないように変更する。上記のループは、以下のようになる。for ( nj=0; nj<ib; nj++) { … }
並列化の方針(C言語)
1. 全PEで行列AをN×Nの大きさ、ベクトルx、yをNの大きさ、確保してよいとする。
2. 各PEは、担当の範囲のみ計算するように、ループの開始値と終了値を変更する。ブロック分散方式では、以下になる。(N が nprcで割り切れる場合)
ib = N / nprcdo nj=myid*ib+1, (myid+1)*ib …. enddo
3. (2の並列化が完全に終了したら)各PEで担当のデータ部分しか行列を確保しないように変更する。上記のループは、以下のようになる。
do nj=1, ib …. enddo
並列化の方針(Fortran言語)
並列化の方針(行列-ベクトル積)(C言語)
PE0
PE1
PE2
PE3
for ( nj=0; nj<(N/4); nj++) { 内積( nj, ni ) }
for ( nj=(N/4); nj<(N/4)*2; nj++) { 内積( nj, ni ) }
for ( nj=(N/4)*2; nj<(N/4)*3; nj++) { 内積( nj, ni ) }
for ( nj=(N/4)*3; nj<N; nj++) { 内積( nj, ni ) }
※各PEで使われない領域が出るが、担当範囲指定がしやすいので実装がしやすい。
全PEでN×N行列を持つ場合
並列化の方針(行列-ベクトル積)(Fortran言語)
PE0
PE1
PE2
PE3
do nj=1, N/4 内積( nj, ni )
enddo
do nj=N/4+1, (N/4)*2内積( nj, ni )
enddo
do nj=(N/4)*2+1, (N/4)*3内積( nj, ni )
enddo
do nj=(N/4)*3+1, N内積( nj, ni )
enddo
全PEでN×N行列を持つ場合
※各PEで使われない領域が出るが、担当範囲指定がしやすいので実装がしやすい。
並列化の方針(行列-ベクトル積)
PE0
PE1
PE2
PE3
=
=
=
=
この方針では、y=Axのベクトルyは、以下のように一部分しか計算されないことに注意!
並列化時の注意
演習環境は、最大288PEです。 並列化は、<できた>と思ってもバグっていることが多い! このサンプルの検証部分(debug=1の時に実行される部分)は、PE0がベクトルyの要素すべてを所有することが前提となっています。
出力結果を考慮して検証部分も並列化してください。
Nを小さくして、printfで結果(ベクトルy)を目視することも、デバックになります。しかし、Nを目視できないほど大きくする場合にバグることがあります。目視のみデバックは、経験上お勧めしません。
数学ライブラリ開発では、できるだけ数学(線形代数)の知識を利用した方法で、理論的な解と結果を検証することをお勧めします。
時間計測方法(C言語)
double t0, t1, t2, t_w;
..
ierr = MPI_Barrier(MPI_COMM_WORLD);
t1 = MPI_Wtime();
<ここに測定したいプログラムを書く>
t2 = MPI_Wtime();
t0 = t2 - t1;
ierr = MPI_Reduce(&t0, &t_w, 1,
MPI_DOUBLE,MPI_MAX, 0,
MPI_COMM_WORLD);
バリア同期後、時間を習得し保存
各プロセッサ(プロセス)で、t0の値は異なる。
この場合は、最も遅いものの値をプロセスID = 0(rank0)が受け取る。
double precision t0, t1, t2, t_w
double precision MPI_WTIME
..
call MPI_BARRIER(MPI_COMM_WORLD, ierr)
t1 = MPI_WTIME(ierr)
<ここに測定したいプログラムを書く>
t2 = MPI_WTIME(ierr)
t0 = t2 - t1
call MPI_REDUCE(t0, t_w, 1,
& MPI_DOUBLE_PRECISION,
& MPI_MAX, 0, MPI_COMM_WORLD, ierr)
時間計測方法(Fortran)
バリア同期後、時間を習得し保存
各プロセッサ(プロセス)で、t0の値は異なる。
この場合は、最も遅いものの値をプロセスID = 0(rank0)が受け取る。
サンプルプログラムの起動
4.総和演算プログラムの実行
Samples.tarの中身
• Hello/
• 並列版Helloプログラム
• Mat-vec/
• 行列・ベクトル積の計算プログラム
• MPI/
• MPI集団通信のサンプルプログラム
• Wa1/
• 逐次転送方法による総和演算プログラム
• Wa2/
• 二分木通信方式による総和演算プログラム
総和演算プログラム(Wa1, 逐次転送方式)
各MPIプロセスが所有するデータを、全プロセスで加算し、あるプロセス1つが結果を所有するという処理を考える。
素朴な方法(逐次転送方式)1. (rankIDが0でなければ)左隣のプロセスからデータを受信2. 左隣のプロセスからデータが来ていたら
1. 受信する2. <自分のデータ>と<受信データ>を加算する3. (255番でなければ)右隣のプロセスに<2の結果>を送信する4. 処理を終了する
実装上の注意• 左隣とは、(myid-1)のIDをもつプロセス• 右隣とは、(myid+1)のIDをもつプロセス
• myid=0のプロセスは左隣はないので、受信はしない• myid=p-1のプロセスは右隣はないので、送信はしない
逐次転送方式(バケツリレー方式)による加算
CPU0 CPU1 CPU2 CPU3
0 1 2 3
0
所有データ
0 + 1 = 1
1
1 + 2 = 3
3
3 + 3 = 6
送信 送信 送信
最終結果
所有データ 所有データ 所有データ
1対1通信利用例(Wa1, 逐次転送方式、C言語)
void main(int argc, char* argv[]) {
…
MPI_Status istatus;
…
dsendbuf=(double)myid;
drecvbuf=0.0;
if(myid!=0){
ierr=MPI_Recv(&drecvbuf,1,MPI_DOUBLE,myid-1,0,MPI_COMM_WORLD,&istatus);
}
dsendbuf=dsendbuf+drecvbuf;
if(myid!=nprc-1){
ierr=MPI_Send(&dsendbuf,1,MPI_DOUBLE,myid+1,0,MPI_COMM_WORLD);
}
if(myid==nprc-1) printf("Total=%4.2lf¥n",dsendbuf);
…
}
受信用システム配列の確保
自分より一つ少ないID番号(myid-1)から、double
型データ1つを受信しdrecvbuf変数に代入
自分より一つ多いID番号(myid+1)に、dsendbuf変数に入っているdouble型データ1つを送信
program main
…
integer istatus(mpi_status_size)
…
dsendbuf=dble(myid)
drecvbuf=0.0d0
if(myid/=0) then
call mpi_recv(drecvbuf,1,mpi_double_precision,myid-1,0,&
mpi_comm_world,istatus,ierr)
endif
dsendbuf=dsendbuf+drecvbuf
if(myid/=nprc-1) then
call mpi_send(dsendbuf,1,mpi_double_precision,myid+1,0,&
mpi_comm_world,ierr)
endif
if(myid==nprc-1) then
print *,"Total = ",dsendbuf
endif
…
end program main
1対1通信利用例(Wa1, 逐次転送方式、Fortran)
受信用システム配列の確保
自分より一つ少ないID番号(myid-1)から、double
precision型データ1つを受信しdrecvbuf変数に代入
自分より一つ多いID番号(myid+1)に、dsendbuf変数に入っているdouble precision型データ1つを送信
総和演算プログラム(Wa2, 二分木通信方式)
二分木通信方式(256プロセスでの計算であることに注意)1. nj = 1;2. for (ni=0; ni < log2(nprc); ni++) 3. if ( (myid & nj) == nj)
• (myid – nj)番プロセスからデータを受信;• 自分のデータと、受信データを加算する;• nj = nj * 2;
4. else • (myid + nj)番プロセスに、データを転送する;• 処理を終了する;
総和演算プログラム(Wa2, 二分木通信方式)
0 1 2 3 4 5 6 71段目
1 3 5 72段目
3 73段目=log2(8)段目
0 1 2 3 4 5 6 7
1 3 5 7
3 7
7
総和演算プログラム(Wa2, 二分木通信方式)
実装上の工夫• 要点:プロセス番号の2進数表記の情報を利用する• 第ni段において、受信するプロセスの条件は、以下で書ける:
myid & nj が nj と一致ここで、nj = 2^(ni-1) 。つまり、プロセス番号の2進数表記で右からni番目のビットが立っているプロセスが、送信することにする
• また、送信元のプロセス番号は、以下で書ける:myid + nj
つまり、通信が成立するプロセス番号の間隔は2^(ni-1)←二分木なので• 送信プロセスについては、上記の逆が成り立つ。
総和演算プログラム(Wa2, 二分木通信方式)
逐次転送方式の通信回数明らかに、nprocs-1 回
二分木通信方式の通信回数• 見積もりの前提
• 各段で行われる通信は、完全に並列で行われる(通信の衝突は発生しない)
• 段数の分の通信回数となる• つまり、log2(nprocs) 回
両者の通信回数の比較• プロセッサ台数が増すと、通信回数の差(=実行時間)がとても大きくなる
• 1024プロセス構成では、1023回 対 10回!
• でも、必ずしも二分木通信方式がよいとは限らない(通信衝突の多発)
レポート課題(II)
3. 逐次転送方式、2分木通信方式の実行時間を計測(MPI_Wtime
関数の利用)し、どの方式が何台のプロセッサ台数で有効となるかを明らかにせよ。また、その理由について考察せよ。
4. 二分木通信方式について、プロセッサ台数が2のべき乗でないときにも動作するように、プログラムを改良せよ。
5. MPIとOpenMPとは何か?それぞれについて説明せよ。
下記について、レポートとしてまとめよ。
Reedbushを利用する上での注意
Reedbushを利用する上で不明な点があったら、ポータル上にマニュアルがありますので、それを参考にしてください。
https://reedbush-www.cc.u-tokyo.ac.jp/
それでも解決しない場合は、まずは松本に聞いてください。情報基盤センターへ直接問い合わせることは止めてください。
この先、計算科学概論でReedbushを使うか使わないかは各先生によって異なります。使う先生もいれば、使わない先生もいます。
Reedbushのアカウントは(おそらく)8月末で消滅しますので、それまでにバックアップ等が必要な方は各自の責任でお願いします。
アカウントが消滅するまでは自由に使えますので、余力のある人はGPUを使った計算などにも是非チャレンジしてみて下さい。(何も説明しないけど。)