共有メモリを使ったデータ交換と同期 �
慶應義塾大学理工学部
天野英晴
hunga@am.ics.keio.ac.jp
同期の必要性
Fork-join: 並列プロセスの開始と終了
fork
forkで生まれたプロセス(スレッド)間は
メモリを共有
joinで全プロセスの待ち合わせをする
→同期を取っている
簡単な並列プログラムはfork/joinのみ
で制御できる→OpenMP
fork
join
join
排他制御
→しかしこれはうまく行かない。なぜか?
P1とP2が同時に変数を読んだら?
0
P1
1.xを読み出す
2.0かどうかをチェック
3.1を書きこむ
P2
1.xを読み出す
2.0かどうかをチェック
3.1を書きこむ
P1がxを読んで0かどうかをチェックしている間に、P2がxを読み出すかも
しれない→P1,P2共に0を取ることができる。
読む操作と書く操作を不可分(Atomic/Indivisible)に行う命令が必要
→不可分命令
Test & Set (x)
0
P1
同時に命令が実行されても、必ず一つだけ0を読み、他は1にする。
→ハードウェアの支援が必要
他にもSwap, Compare&Swap, Fetch&Dec, Fetch&Addなど色々
あるが、原理は同じ
P2
Test&Set(x)
Test&Set(x)
xを読み出す。
1を書きこむ
2つの操作を不可分に行う
この間共有メモリを占有
Critical Sectionの実行
Test&Set(x)=0?
No.
Yes.
Critical Section
この例ではプリンタを使う
x = 0
忘れずにx=0にしておく.
一つだけが実行できる領域
不可分命令があればCritical Sectionが作れる→なんでもできる!
でもちょっと使いにくい
バリア同期
Barrier;
Barrier;
Barrier;
バリア成立
.
バリア同期は不可分命令があれば作れるが、専用の
ハードウェアを使う場合もある
バリア成立を待つ
P1
P2
P3
OpenMP:�最も簡単なマルチコア利用法
OpenMPの実行モデル
Block A
#pragma omp parallel
{
Block B
}
Block C
Block A
Block B
Block B
Block B
…
Block C
Thread fork
Thread join
Master Thread
環境変数: OMP_NUM_THREADS で実行スレッド数を設定
Parallel Region
for 文
# pragma omp parallel
{
#pragma omp for
for(i=0; i<1000; i++) {
c[i]=a[i]+b[i];
}
}
# pragma omp parallel for
for(i=0; i<1000; i++) {
c[i]=a[i]+b[i];
}
反復は各スレッドに等分に
割り当てられる
sections 文
#pragma omp parallel sections
{
#pragma omp section
sub1();
#pragma omp section
sub2();
#pragma omp section
sub3();
}
sub1
sub2
sub3
Thread join
三つの違った処理が並列に実行され、
終了時に同期される
private sub-directive
# pragma omp parallel for firsptrivate(c)
for(i=0; i<1000; i++) {
d[i]=a[i]+c*b[i];
}
c は各スレッドにコピーされる → 高速実行が可能
firstprivateで初期化
private sub-directiveの利用
# pragma omp parallel for private(j)
for(i=0; i<100; i++) {
for(j=0; j<100; j++)
a[i]=a[i]+amat[i][j]*b[j];
}
shared: 全スレッドで共有
private:各スレッドで用意、初期化はしない
firstprivate: 各スレッドで用意、初期化される
この文をprivate(j)なしに実行したらどうなるだろう?→
全てのスレッドでjが更新される→エラー!
reduction sub-directive
# pragma omp parallel for reduction(+:ddot)
for(i=0; i<100; i++) {
ddot+= a[i]*b[i];
}
リダクション演算は、データを足し込んでいく演算。
良く用いられるが、並列実行は、このsub-directiveを使わないと
難しい
組み込み関数
#include <omp.h>
int nth, myid;
nth = omp_get_num_threads();
myid = omp_get_thread_num();
時間を計る: omp_get_wtime();
#include <omp.h>
double ts, te;
ts = omp_get_wtime();
実行
te = omp_get_wtime();
printf(“time[sec]:%lf\n”,te-ts);
他のpragma
#pragma omp single
{ blocks….. }
指定されたブロック内の文を単一スレッドに割り当てる
#pragma omp master
{ blocks..... }
指定されたブロック内の文をマスタースレッドに割り当てる。
OpenMPを使ってみる
ssh login_name@XXXX.educ.cc.keio.ac.jp
https://keio.box.com/s/zd3i2rhx0254bkqms7ky5lq5jbojy2yu
自分の学籍番号に割り当てられたマシンにログインしてください
コンパイルと実行
gcc –fopenmp hello.c –o hello
./hello
Hello OpenMP world from 2 of 8
….
ここではスレッド数は8に設定されている。
これは、環境変数OMP_NUM_THREADS をコマンドラインで設定することで変更できる。
例
export OMP_NUM_THREADS=6
./reduct
じっさいのコア数を超える設定も可能だが速くならない
例題プログラムreduct4k.c
演習 CG法
演習で困ったら
https://uguisu.skr.jp/Windows/vi.html など
windowは開かないが同様に使える
https://uguisu.skr.jp/Windows/emacs.htmlなど
https://www.st.itc.keio.ac.jp/ja/com_remote_winscp_st.html
マシンの割り当て
まとめ