PowerPoint

全体ミーティング (9/12)
村田 雅之
今日の内容
• 修士論文について
– Deterministic Parallel Copying Garbage Collection
• 先日の発表と基本的には同じになります
並列プログラムの非決定性
• スレッドの実行順序により結果が非決定的に
なること
– バグの原因になる
– デバッグが困難である
非決定性の例
Thread 1
x=3
Thread2
Thread 1
x=3
Thread2
x=x*2;
x=x+1;
x=4
x=6
x=x*2;
x=8
x=x+1;
x=7
非決定性の例
Thread 1
x=3
Thread2
Thread 1
x=3
Thread2
x=x*2;
x=x+1;
x=4
x=6
x=x*2;
x=x+1;
x=8
x=7
非決定的
ガベージコレクション (GC)
• 不要なメモリ領域を再利用するアルゴリズム
• GCの実行はユーザプログラムの性能に
影響を与えるので高速化したい
• 並列化すれば高速化が期待できる
並列GCの難しさ
• 実装が難しい
• GCの正しさの検証は複雑
– 必要なデータがすべてコピーされるか、等
• 並列化すると検証はもっと難しくなる
– 前述の非決定性が絡む
並列GCの難しさ
• 実装が難しい
• GCの正しさの検証は複雑
– 必要なデータがすべてコピーされるか、等
• 並列化すると検証はもっと難しくなる
– 前述の非決定性が絡む
本研究の目標
• 並列GCの決定性を保証する
– GCの実装の正しさの証明を難しくする一因となる
非決定性を排除できる
本研究で提案する手法
• 並列GCのアルゴリズムを考える
• 決定性を検証できる型システムを適用する
DPJ (Deterministic Parallel Java)
• Type and Effect System for Deterministic
Parallel Java
– Bocchino Jr. らが提案 (2009)
• 型システムを用いて静的に決定性を検証
• 実装が公開されている
– http://dpj.cs.uiuc.edu/
DPJの型システムの特徴
• Region
– ヒープ領域を分割する
– 一つのスレッドからしかアクセスしないようにする
– 配列を分割して別のregionに配置することも可能
• Effect
– スレッドのregionへのアクセスを表す型情報
Region
• ヒープを分割しデータを配置する
– 図では変数xをregion Aに変数yをregion Bに配置
• ひとつのregionに複数のスレッドが同時に
アクセスしないよう型システムで保証する
– 決定性を保証できる
ヒープ
region A
region B
x
y
配列の分割
• 配列を分割してそれぞれを別のregionに
配置することができる
region A
配列の分割
• 配列を分割してそれぞれを別のregionに
配置することができる
region A0
region A1
Effect
• プログラム中の文の
regionへのアクセスを表す型情報
• 例
Aを読む、Bに書く
y = x + 1;
x
y
region A
region B
型検査による決定性の検証
• 各スレッドのeffectが異なるregionへのものに
なっていることを型検査で確かめる
– 各スレッドが違うデータにアクセスしていれば
実行順序は結果に影響しない
検証の例
• 配列に書き込みを行う
– 2つの区間に分けて並列処理
• 配列はregion Aに配置されている
region A
配列
並列実行{
write(i,j);
write(k,l);
}
二つの文を並列実行する
Regionを分割しなかったとき
• ともに「Aに書く」というeffectを持つ
– 同じregionに並列にアクセスする
• 型検査によりエラーとなる
– 決定性は保証されない
region A
並列実行{ Aに書く
write(i,j);
write(k,l);
}
Aに書く
Regionを分割したとき
• 並列実行されるスレッドが別のregionへの
effectを持つ
• 型検査を通る
– 決定性が保証される
region A1
region A0
並列実行{ A0に書く
write(i,j);
write(k,l);
}
A1に書く
本研究で行ったこと
• 並列コピーGCのアルゴリズムを考えた
– 逐次のコピーGCのアルゴリズムを拡張する
• DPJの型検査によって決定性を検証した
コピーGC
• ヒープを二つにわけて埋まってきたら必要な
ものだけコピーすることでGCを行う
– プログラムは普段は一方を使用
– 必要なもの = rootから到達可能なもの
From
To
コピーGC
• ヒープを二つにわけて埋まってきたら必要な
ものだけコピーすることでGCを行う
– プログラムは普段は一方を使用
– 必要なもの = rootから到達可能なもの
From
To
並列コピーGCのアルゴリズム
copy(from,to){
if(from,toが十分短ければ){
逐次コピーGCを行う
}else{
from,toを分割
並列実行{ // 再帰呼び出し
copy(fromの前半, toの前半);
copy(fromの後半, toの後半);
}
返ってきた結果を統合
}
}
終了後にデフラグ
並列コピーGCのアルゴリズム
copy(from,to){
if(from,toが十分短ければ){
逐次コピーGCを行う
STEP 1
}else{
大きなヒープの分割
from,toを分割
並列実行{ // 再帰呼び出し
copy(fromの前半, toの前半);
copy(fromの後半, toの後半);
}
返ってきた結果を統合
}
2つを並列実行する
}
終了後にデフラグ
並列コピーGCのアルゴリズム
copy(from,to){
STEP 2
if(from,toが十分短ければ){
各区間について
逐次コピーGCを行う
コピーGCをする
}else{
from,toを分割
並列実行{ // 再帰呼び出し
copy(fromの前半, toの前半);
copy(fromの後半, toの後半);
}
返ってきた結果を統合
}
}
終了後にデフラグ
並列コピーGCのアルゴリズム
copy(from,to){
if(from,toが十分短ければ){
逐次コピーGCを行う
}else{
from,toを分割
並列実行{ // 再帰呼び出し
copy(fromの前半, toの前半);
copy(fromの後半, toの後半);
}
STEP 3
返ってきた結果を統合
分割した結果を
}
ひとつにまとめる
}
終了後にデフラグ
並列コピーGCのアルゴリズム
copy(from,to){
if(from,toが十分短ければ){
逐次コピーGCを行う
}else{
from,toを分割
並列実行{ // 再帰呼び出し
copy(fromの前半, toの前半);
copy(fromの後半, toの後半);
}
返ってきた結果を統合 STEP 4
}
データを移動させて
}
隙間を減らす
終了後にデフラグ
4つのステップ
1.
2.
3.
4.
ヒープを分割する
分割したヒープそれぞれで並列にコピーGC
分割した結果をまとめる
データの移動
STEP 1: ヒープの分割
• 大きなヒープをあるサイズ以下になるまで
並列かつ再帰的に分割
– 分割したヒープをそれぞれ別のregionに配置する
ことで決定的な並列実行ができる
• 十分小さくなったら逐次コピーGCを行う
STEP 2: 逐次コピーGC
• 基本的には逐次アルゴリズムと同様
• ただし分割されたregionの外へのポインタは
後回しにする
例: STEP 1
ヒープの分割
• ヒープを2分割するケース
From
To
例: STEP 1
ヒープの分割
• From, Toをそれぞれ2分割し別のregionとする
From0
From1
To0
To1
例: STEP 2
逐次コピーGC
• 2つに分けたregionで並列にコピーGCを行う
From0
From1
To0
To1
例: STEP 2
逐次コピーGC
• ただし、regionの外へのポインタがあれば
それを記憶して後で追跡する
From0
From1
To0
To1
STEP 3: 分割したregionの統合
• 後回しにしていた、regionの外へのポインタを
再び追跡する
例: STEP3
分割したregionの統合
• 統合前の状態
From0
From1
To0
To1
例: STEP3
分割したregionの統合
• Regionが統合される
From
To
例: STEP3
分割したregionの統合
• 後回しにしていたポインタから新たに到達
可能になるデータをコピー
From
To
例: STEP3
分割したregionの統合
• ここでは新しいデータは前から詰めていく
– 空き領域をリストで管理している
From
To
例: STEP3
分割したregionの統合
• これで必要なデータのコピーが終わる
From
To
まだ不満なこと
• ヒープを分割するのでデータが連続して
配置されない
From
To
隙間が空いている
STEP 4: デフラグを行う
• データを移動して間を詰める
– 末尾に大きく連続した空き領域ができる
• 本研究では単純な方法をとる
– 後ろの断片から順にできるだけ前に移動
デフラグをするメリット
• 単純なメモリ管理ができる
– データをうしろに配置していくだけ
– 逐次のコピーGCで可能だったこと
例: STEP4
デフラグ
• このような状態のヒープがあるときを考える
例: STEP4
デフラグ
• 一番うしろから動かそうとする
– 末尾の空き領域を大きくしたいので
例: STEP4
デフラグ
• 移動可能な空き領域を前から探していく
– この場合一番最初で見つかる
例: STEP4
デフラグ
• 移動可能な領域を移動する
– 移動を並列化することで高速化する
例: STEP4
デフラグ
• 以上の手続きをできるだけ繰り返す
例: STEP4
デフラグ
• 以上の手続きをできるだけ繰り返す
デフラグの注意点
• データを移動させると間違った場所を指す
ポインタが生まれる
もともとのポインタ
デフラグの注意点
• データを移動させると間違った場所を指す
ポインタが生まれる
データを移動した
間違ったポインタ
間違ったポインタの修正
• データ移動の履歴を記憶しておいて移動後に
間違ったポインタを修正する
– 修正は並列に行うことで高速化する
この移動の情報を覚えておいて
後でポインタを修正する
このアルゴリズムの決定性の検証
• DPJ言語でこのアルゴリズムを記述する
• DPJの型検査により決定性が検証される
実験
• 本研究の並列GCの正しさを確認する
– 単純なデータについて確認
• 性能を測定する
– スケーラビリティ
– デフラグの効果
• 環境
– 6-core Opteron 2.80 GHz * 2 (12 コア), 64GB RAM
– Linux, Java 1.6.0 update 18, DPJ version 1.7.0
本研究の並列GCの正しさの確認
• 単純なモデルについて結果を検証
– すべてコピーされることが予想されるヒープに
このアルゴリズムを適用する
• 実際にすべてのデータがコピーされている
ことを確認
• 形式的検証は難しいのでできなかった
性能測定
• スケーラビリティ
• デフラグの効果
• ヒープの仮想的なモデルを作成
• そのモデルについてコピーGCを行う
ヒープの仮想的モデル
• 2つのポインタを持つオブジェクトを配置
• ポインタは一定距離より近いところを指す
– 局所性がある
• サイズは230 (= 1G)
スケーラビリティの評価
• 実行時間を計測
– ヒープを16分割してGCを行う
– 約40%が生きているデータ
– ワーカースレッドの数を1から12まで変化させる
• 環境
– 6-core Opteron 2.80 GHz * 2 (12 コア), 64GB RAM
– Linux, Java 1.6.0 update 18, DPJ version 1.7.0
実験結果
• 逐次コピーGCに対して3.2倍速い
– 12スレッド使用時
• 1スレッドの場合に対して7.3倍速い
Speed to sequential algorithm
3.5
3
2.5
2
Parallel
1.5
Sequential
1
0.5
0
1
2
3
4
5
6
7
8
9
Number of worker threads
10
11
12
実験結果(コピーとデフラグ)
• コピーの速度は12スレッドで5.5倍
– 1スレッドの時に対して
– 分割区間外へのポインタで並列性が下がる
• デフラグの速度は11.3倍
60
50
Time (sec)
40
30
Copying
Defrag
20
10
0
1
2
3
4
5
6
7
8
Number of worker threads
9
10
11
12
コピーGCとデフラグの時間
• コピーは分割すると速くなる
– 16分割までは特に
• 12 coresなので
• デフラグは分割が増えると非常に重くなる
70
60
Time (sec)
50
40
Defrag
30
Copying
20
10
0
1
2
4
8
16
32
Number of divided regions
64
128
256
条件を変えてみたとき
• ポインタがさしている距離を広くする
– 範囲外へのポインタが増える
実験結果
• 速度が伸びない
– 12スレッド使って1.5倍弱
1.6
Speed to sequential algorithm
1.4
1.2
1
0.8
Parallel
0.6
Sequential
0.4
0.2
0
1
2
4
6
Number of worker threads
8
12
実験結果
• コピーの並列性が低いためと考えられる
14
12
Speedup
10
8
Copy
6
Defrag
4
2
0
1
2
4
6
Number of worker threads
8
12
さらに別のケース
• ワーカースレッドの数と分割数をあわせる
• 1, 2, 4, 8, 12スレッドで実験
– 12のときのみ16分割
結果
• 8スレッドで3倍弱
– 12スレッドでも同じくらい
• 16分割のため遅くなる
3.5
Speed to sequential
3
2.5
2
Parallel
1.5
Sequential
1
0.5
0
1
2
3
4
5
6
7
8
Number of worker threads
9
10
11
12
結果
• コピーだけなら4.5倍程度
• デフラグの時間はあまり変わっていない
– 問題は複雑になるがスレッド数が増えるため?
50
45
40
Time (sec)
35
30
25
Copy
20
Defrag
15
10
5
0
1
2
3
4
5
6
7
8
Number of worker threads
9
10
11
12
ヒープの長さを変えてみたとき
• だいたい線型
• 並列コピー部分は増えにくい
100
Time (sec)
10
Copying
1
Defrag
Total
Sequential
0.1
0.01
1
2
4
8
16
32
64
Size of Heap (M)
128
256
512
1024
ヒープの長さを変えてみたとき
• 逐次と並列の時間の関係が変わっている
– キャッシュの効果?
100
Time (sec)
10
Copying
1
Defrag
Total
Sequential
0.1
0.01
1
2
4
8
16
32
64
Size of Heap (M)
128
256
512
1024
デフラグの効果
• すべての空き領域に対する
末尾にある空き領域の割合
末尾にある空き領域
– 理想的には100%
• 生きているデータの割合と分割区間数を変化
– 20, 40, 60%
– 1, 2, 4, 8, … , 256分割
測定結果
• 20, 40%では約9割の空き領域が末尾にある
• 60%では非常に悪い
– 使用領域>空き領域のためデータ移動が困難
100
Ratio of free spaces at the tail (%)
90
80
70
60
20%
50
40%
40
60%
30
20
10
0
1
2
4
8
16
32
Number of divided regions
64
128
256
測定結果
• 分割を増やしたところで改善している
• ピースが小さくなって移動が容易になるため
100
Ratio of free spaces at the tail (%)
90
80
70
60
20%
50
40%
40
60%
30
20
10
0
1
2
4
8
16
32
Number of divided regions
64
128
256
関連研究: GCの形式的検証
• Automated Verification of Practical Garbage
Collectors.
– Hawblitzel and Petrank, 2009
– 正しさや安全性に関する条件式を含めてGCの
アルゴリズムを記述する
– そこから得られた条件式をTheorem Proverを
用いて解くことで性質を検証する
– 並列GCの決定性については扱わない
関連研究: 並列GC
• Parallel Garbage Collection for Shared
Memory Multiprocessors
– Flood et al., 2001
– ヒープを分割して行う並列コピーGC
– ヒープ分割による隙間はそのまま
– 8コアで5倍前後の高速化
• 条件の差異のため単純比較はできない
– 決定性についての考察はされていない
Future Works
• 並列GCの検証に形式的な証明を与える
• アルゴリズムの改善
– コピーGCの速度向上
– デフラグの効果を高める
– 現実的な構造のヒープについて効果的か?
並列GCの形式的検証
• 決定性を検証して、逐次環境での正しさを
検証すれば並列GCの正しさの検証になる
– 並列環境と逐次環境の結果は決定的
• 本研究の並列アルゴリズムは決定的なので
あとは逐次環境での形式的検証が必要
まとめ
• 並列なコピーGCのアルゴリズムを考えた
• そのアルゴリズムにDPJの型検査を適用し
決定性を検証した
• 単純なモデルについては正しさを確認した
• 性能測定を行った
– 12コアで逐次コピーGCより3.2倍速い