並列化による高速化 
押川令
目次 
・並列化とは 
・どのくらい速くなるのか 
・並列化を容易にする 
1.再帰的 
2.繰り返し 
・並列化してできるだけ速くするには 
(まとめ)
並列化とは 
並行=システムが複数の動作を同時に実行状 
態に保てる機能を持っていること 
マルチコアプロセッサ=複数のコアで複数の 
スレッドを実行し、その分だけ性能が上がる 
逐次アプリケーション(単スレッド)を並行アプ 
リケーションへと変換する作業を並列化と呼 
ぶことにする。 
コア=CPU内部の命令を実行するハードウェア 
スレッド=分割した処理
どのくらい速くなるのか? 
同じ処理を逐次実行した場合と、使用するス 
レッド数を変えて並列実行した場合では処理速 
度はどれくらい違うのだろうか? 
高速化率=(逐次実行時間) / (並行実行時間) 
を指標として調べてみる。
どのくらい速くなるのか? 
サンプル: 
π(の近似値)を数値積 
分で求める 
(積分範囲を分割した長方形の高さ 
をそれぞれ求め合計したものと、 
長方形の幅を掛ける) 
逐次プログラム 
static long num_rects = 100000000; 
(今回は一億個に分割する) 
int main(){ 
double area; 
double mid,height,width,sum=0.0; 
int i; 
width=1.0/(double)num_rects; 
for(i=0;i<num_rects;i++){ 
mid=(i+0.5)*width; 
height=4.0/(1.+mid*mid); 
sum+=height; 
} 
area= width*sum; 
printf(“πの値%fn",area); 
return 0; 
}
どのくらい速くなるのか? 
サンプル: 
π(の近似値)を数値積 
分で求める 
(積分範囲を分割した長方形の高さ 
をそれぞれ求め合計したものと、 
長方形の幅を掛ける) 
static long num_rects = 100000000; 
(今回は一億個に分割する) 
int main(){ 
double area; 
double mid,height,width,sum=0.0; 
int i; 
width=1.0/(double)num_rects; 
#pragma omp parallel for private(mid,height) reduction(+:sum) 
for(i=0;i<num_rects;i++){ 
mid=(i+0.5)*width; 
height=4.0/(1.+mid*mid); 
sum+=height; 
} 
area= width*sum; 
printf(“πの値%fn",area); 
return 0; 
} 
OpenMPを用いて並列化 
omp_set_num_threads()で 
使用するスレッド数を変えて 
繰り返し実行 
それぞれの処理速度等を出力する 
(コード省略) 
並行プログラム
どのくらい速くなるのか? 
こんな感じで出てくる
どのくらい速くなるのか? 
こんな感じで出てくる 
スレッド数処理時間(ミリ秒) 高速化率 
1 1857 1 
2 970 1.914433 
3 650 2.856923 
4 514 3.61284 
5 416 4.463942 
6 350 5.305714 
7 320 5.803125
どのくらい速くなるのか? 
こんな感じで出てくる 
7 
6 
5 
4 
3 
2 
1 
0 
1 2 3 4 5 6 7 
高 
速 
化 
率 
スレッド数 
サンプル
どのくらい速くなるのか? 
Amdahl(アムダール)の法則(の一つ) 
高速化率≤1/(1-並列実行可能時間の割合) 
+ 
(並列実行可能時間の割合/スレッド数)
どのくらい速くなるのか? 
Amdahl(アムダール)の法則(の一つ) 
高速化率≤1/(1-並列実行可能時間の割合) 
+ 
(並列実行可能時間の割合/スレッド数) 
つまり、100%並列化すれば、高速化率はスレッド 
数の増加に比例する 
※スレッド管理などのオーバーヘッドを無視
どのくらい速くなるのか? 
さっきのグラフ 
7 
6 
5 
4 
3 
2 
1 
0 
1 2 3 4 5 6 7 
高 
速 
化 
率 
スレッド数 
サンプル 
100% 
(単純計算で並列化の割合は95%くらい)
並列化を容易にする 
並列化するには前の処理に依存しない独立し 
た処理が必要 
 並列化に向かないアルゴリズムもある 
 時にはアルゴリズムの変更も必要
並列化を容易にする 
サンプル:クイックソート 
4 0 2 7 6 9 3 1 5 8
並列化を容易にする 
サンプル:クイックソート 
4 0 2 7 6 9 3 1 5 8
並列化を容易にする 
サンプル:クイックソート 
4 0 2 7 6 9 3 1 5 8 
1 0 2 3 4 7 6 9 5 8
並列化を容易にする 
サンプル:クイックソート 
4 0 2 7 6 9 3 1 5 8 
1 0 2 3 4 7 6 9 5 8
並列化を容易にする 
サンプル:クイックソート 
4 0 2 7 6 9 3 1 5 8 
1 0 2 3 4 7 6 9 5 8 
0 1 2 3 4 6 5 7 8 9
並列化を容易にする 
サンプル:クイックソート 
4 0 2 7 6 9 3 1 5 8 
1 0 2 3 4 7 6 9 5 8 
0 1 2 3 4 6 5 7 8 9 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム
1.再帰的 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム
1.再帰的 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム
1.再帰的 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム 
通常再帰的に実装される 
⇒そのまま並列化してみる
1.再帰的 
ピボット要素によって分割された2パーティ 
ションは独立してソートできる 
⇒再帰コールのたびにそれを行う新たなスレッ 
ドを作成する
1.再帰的 
4 0 2 7 6 9 3 1 5 8 
1 0 2 3 4 7 6 9 5 8 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
スレッドを作る 
4 0 2 7 6 9 3 1 5 8 
1 0 2 3 4 7 6 9 5 8 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
スレッドを作る 
4 0 2 7 6 9 3 1 5 8 
スレッドを作る 
1 0 2 3 4 7 6 9 5 8 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
スレッドを作る 
4 0 2 7 6 9 3 1 5 8 
スレッドを作る 
1 0 2 3 4 7 6 9 5 8 
スレッドを作る 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
スレッドを作る 
4 0 2 7 6 9 3 1 5 8 
スレッドを作る 
1 0 2 3 4 7 6 9 5 8 
スレッドを作るスレッドを作る 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
スレッドを作る 
4 0 2 7 6 9 3 1 5 8 
スレッドを作る 
1 0 2 3 4 7 6 9 5 8 
スレッドを作るスレッドを作る 
0 1 2 3 4 5 6 7 8 9 
スレッドを作る 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
スレッドを作る 
4 0 2 7 6 9 3 1 5 8 
スレッドを作る 
スレッドを作る 
1 0 2 3 4 7 6 9 5 8 
スレッドを作るスレッドを作るスレッドを作るスレッドを作る 
0 1 2 3 4 5 6 7 8 9 
スレッドを作るスレッドを作るスレッドを作る 
0 1 2 3 4 5 6 7 8 9
1.再帰的 
作成するスレッドの数 
パーティションに分割するたびに1スレッド作成 
する 
→最低でも1要素につき1スレッドを作成する 
⇒オーバーヘッドが多いため速度が向上しにくい 
下の2スレッドがソートを完了するまでブロック 
して待っていなければならない 
再帰を用いた逐次アルゴリズムは並列化に向かない
2.繰り返し 
キュー上に演算実行ループと処理状態(パー 
ティションの情報)を置くことによって再帰を 
シミュレートする
2.繰り返し 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム 
キュー
2.繰り返し 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム 
キュー 
パーティションの範囲のデータ 
キューが空になるまで繰り返す
2.繰り返し 
パーティションごとの基本操作 
4 0 2 7 6 9 3 1 5 8 
調べる 
大きい 
パーティション 
小さい 
入れ替え 
ピボットより 
逐次プログラム 
キュー 
パーティションの範囲のデータ 
キューが空になるまで繰り返す 
並列化してみる
2.繰り返し 
キュー上に演算実行ループと処理状態(パーティ 
ションの情報)を置くことによって再帰をシミュ 
レートする 
⇒キュー内のパーティションはどんな順番で、ど 
のスレッドが同時にソートしても依存関係がない 
スレッドを複数用意(スレッドプール)しておき、 
パーティションに分割された順にソートできる
2.繰り返しースレッドへの処理分配 
キュー 
処理の見つけ方を変える 
逐次プログラムの時はキューに要素があれば 
それを取り出していたが… 
スレッドスレッドスレッド 
スレッド 
参照 
キューに要素がある
2.繰り返しースレッドへの処理分配 
キュー 
処理の見つけ方を変える 
逐次プログラムの時はキューに要素があれば 
それを取り出していたが… 
スレッドスレッドスレッド 
スレッド 
キューに要素がある 
参照
2.繰り返しースレッドへの処理分配 
キュー 
処理の見つけ方を変える 
逐次プログラムの時はキューに要素があれば 
それを取り出していたが… 
スレッドスレッドスレッド 
スレッド 
キューに要素がある 
データ
2.繰り返しースレッドへの処理分配 
キュー 
処理の見つけ方を変える 
逐次プログラムの時はキューに要素があれば 
それを取り出していたが… 
スレッドスレッドスレッド 
スレッド 
キューに要素がある 
データ 
空のキューからデータを 
取り出してしまう
2.繰り返しースレッドへの処理分配 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタ 
スレッドスレッドスレッド 
スレッド
2.繰り返しースレッドへの処理分配 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタ 
スレッドスレッドスレッド 
スレッド 
起動しておき、カウンタが0の時はブロックしておく
配列の情報を入れておく 
2.繰り返しースレッドへの処理分配 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタ 
参照 
カウンタが0以外 
スレッドスレッドスレッド 
スレッド 
起動しておき、カウンタが0の時はブロックしておく
2.繰り返しースレッドへの処理分配 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタデクリメント(取り出す予約) 
参照 
カウンタが0以外 
スレッドスレッドスレッド 
スレッド 
起動しておき、カウンタが0の時はブロックしておく
2.繰り返しースレッドへの処理分配 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタデクリメント(取り出す予約) 
参照 
カウンタが0以外 
スレッドスレッドスレッド 
スレッド 
1つパーティションの情報を取り出す 
起動しておき、カウンタが0の時はブロックしておく
2.繰り返しースレッドへの処理分配 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタデクリメント(取り出す予約) 
参照 
カウンタが0以外 
スレッドスレッドスレッド 
スレッド 
1つパーティションの情報を取り出す 
起動しておき、カウンタが0の時はブロックしておく 
基本操作
2.繰り返しースレッドへの処理分配 
キュー 
分割でできた2個のパーティションの 
情報を追加 
同期オブジェクト 
Qの要素数を表す正のカウンタデクリメント(取り出す予約) 
参照 
カウンタが0以外 
スレッドスレッドスレッド 
スレッド 
1つパーティションの情報を取り出す 
起動しておき、カウンタが0の時はブロックしておく 
基本操作
2.繰り返しースレッドへの処理分配 
キュー 
分割でできた2個のパーティションの 
情報を追加 
2増やす 
同期オブジェクト 
Qの要素数を表す正のカウンタデクリメント(取り出す予約) 
参照 
カウンタが0以外 
スレッドスレッドスレッド 
スレッド 
1つパーティションの情報を取り出す 
起動しておき、カウンタが0の時はブロックしておく 
基本操作
2.繰り返しー処理の終了 
処理の終了条件を変える 
逐次プログラムの時はキューが空になるまでだったが… 
キュー 
スレッドスレッドスレッド 
スレッド
2.繰り返しー処理の終了 
処理の終了条件を変える 
逐次プログラムの時はキューが空になるまでだったが… 
キュー 
スレッドスレッドスレッド 
スレッド 
データ
2.繰り返しー処理の終了 
処理の終了条件を変える 
逐次プログラムの時はキューが空になるまでだったが… 
キュー 
スレッドスレッドスレッド 
スレッド 
参照参照 
参照
2.繰り返しー処理の終了 
処理の終了条件を変える 
逐次プログラムの時はキューが空になるまでだったが… 
キュー 
キューは空 
スレッドスレッドスレッド 
スレッド 
参照参照 
参照
2.繰り返しー処理の終了 
処理の終了条件を変える 
逐次プログラムの時はキューが空になるまでだったが… 
キュー 
キューは空 
スレッドスレッドスレッド 
スレッド 
参照参照 
参照
2.繰り返しー処理の終了 
キュー 
同期オブジェクト 
Qの要素数を表す正のカウンタ 
スレッド共有カウンタ 
スレッドスレッドスレッド
2.繰り返しー処理の終了 
キュー 
スレッドスレッドスレッド 
スレッド 
スレッド内の処理中 
・パーティションの要素数が1になった時 
・ピボットでの分割時 
ある1要素が正しい位置になる 
共有カウンタ 
同期オブジェクト 
Qの要素数を表す正のカウンタ
2.繰り返しー処理の終了 
キュー 
スレッドスレッドスレッド 
スレッド 
スレッド内の処理中 
・パーティションの要素数が1になった時 
・ピボットでの分割時 
ある1要素が正しい位置になる 
共有カウンタ 
同期オブジェクト 
Qの要素数を表す正のカウンタ 
インクリメント
2.繰り返しー処理の終了 
キュー 
スレッドスレッドスレッド 
スレッド 
スレッド内の処理中 
・パーティションの要素数が1になった時 
・ピボットでの分割時 
ある1要素が正しい位置になる 
共有カウンタ 
全てのソートが終わると 
共用カウンタの値が要素数 
と等しくなる 
同期オブジェクト 
Qの要素数を表す正のカウンタ
2.繰り返しー処理の終了 
キュー 
スレッド数を加算 
スレッドスレッドスレッド 
スレッド 
スレッド内の処理中 
・パーティションの要素数が1になった時 
・ピボットでの分割時 
ある1要素が正しい位置になる 
共有カウンタ 
全てのソートが終わると 
共用カウンタの値が要素数 
と等しくなる 
同期オブジェクト 
Qの要素数を表す正のカウンタ
2.繰り返しー処理の終了 
キュー 
スレッド数を加算 
スレッドスレッドスレッド 
スレッド 
スレッド内の処理中 
・パーティションの要素数が1になった時 
・ピボットでの分割時 
ある1要素が正しい位置になる 
共有カウンタ 
全てのソートが終わると 
共用カウンタの値が要素数 
と等しくなる 
同期オブジェクト 
Qの要素数を表す正のカウンタ 
参照& 
Break文で脱出
2.繰り返し 
共有データの排他制御などの同期処理が発生 
してしまう 
∧ 
 処理が終わったスレッドから次のパーティ 
ションに移れるのでソート処理量が均等 
 スレッドの作成は最初だけ 
再帰型より効率が良い
並列化してできるだけ速くするには 
並列実行可能時間の割合を上げる 
 実行時間を長く費やしているところから並列 
化する場所を検討する 
 並行性は実現可能な最上位で実装する 
 スレッドごとの処理量を均等にする
並列化してできるだけ速くするには 
並列実行可能時間の割合を上げる 
 実行時間を長く費やしているところから並列 
化する場所を検討する 
 並行性は実現可能な最上位で実装する 
 スレッドごとの処理量を均等にする 
オーバーヘッドを減らす 
 同期処理を減らす 
 スレッド作成や共有データへのアクセスは必 
要だが、できるだけ少なくする
ありがとうございました

並列化による高速化