More Related Content Similar to 冬のLock free祭り safe Similar to 冬のLock free祭り safe (20) More from Kumazaki Hiroki More from Kumazaki Hiroki (15) 冬のLock free祭り safe11. We are the
Bottleneck!
情報工学の発展が追い付いていない!
ヽ('ω')ノ三ヽ('ω')ノもうしわけねぇもうしわけ
ねぇ
19. What is Blocking?
コアが増えるほどに問題が顕著に!
早くしろよ… 早くしろよ…
早くしろよ…
早くしろよ…
早くしろよ… 早くしろよ…
早くしろよ…
早くしろよ…
早くしろよ…
クリティカルセク
ション
25. CASを使ってみよう
Lock-free共有カウンタの例
int x;
void add1(){
int old,new;
do{
old = x;
new = old+1;
失敗してたらやり直す
}while(!cas(&x, old, new));
}
26. CASを使ってみよう
CASのお陰で衝突しても破綻しない
x==1
スレッド スレッド
A B
1. xを読み出す(1) 1. xを読み出す(1)
2. 読んだ値に +1
3. 値が1なら2へCAS 2. 読んだ値に +1
4. 失敗したので再挑戦
5. xを読み出す(2) 3. 値が1なら2へCAS
6. 読んだ値に +1
7. 値が2なら3へCAS 数が合う!
x==3
39. Lock-free Queue の Enque
Enque操作
1. Enqueしたい要素eを用意する
2. 末尾のノードが指すポインタがeを指すようCAS
3. Tailポインタがeを指すようCAS
2ステップ必
要
41. Lock-free Queue の Enque
Tail Head
CAS
構造が破綻
CAS
CAS
CPU1 CPU2
1. 要素eを用意する 1. 要素eを用意する
2. 末尾をCAS 2. 末尾をCAS
3. TailポインタをCAS 3. TailポインタをCAS
47. ロックの目的と効果
Lock-free Stackは不変条件を満たさない瞬間
を外部から観測されないように最小化して
CASで片づけられた
CPU1 CAS CAS
CPU2 CAS CAS
CPU3 CAS
CPU4 CAS CAS
48. ロックの目的と効果
Lock-free Queueは2度CASが要るのでタイミ
ングによって危険
危険な瞬間
CPU1 CAS CAS CAS CAS
CPU2 CAS CAS CAS CAS
CPU3 CAS CAS
CPU4 CAS CAS CAS CAS
49. Lock-free Queueはどうするのか
不変条件
Headがキューの先頭を指す
Tailがキューの末尾を指す
Tail以外のノードは必ず有効なノードを指す
条件を緩め
不変条件
る
Headがキューの先頭を指す
Tailがキューの末尾を指さないかもしれない
Tail以外のノードは必ず有効なノードを指す
52. Lock-free Queueかっこいい!
EnqueとDequeの共同作業!
T deque(){
while(1){
const Node* const first = mHead;
void enque(const T& v){ const Node* const last = mTail;
const Node* const node = new Node(v); const Node* const next = first->mNext;
while(1){ if(first != mHead){ continue;}
const Node* const last = mTail; if(first == last){
const Node* const next = last->mNext; if(next == NULL){
if(last != mTail){ continue; } while(mHead->mNext ==
if(next == NULL){ NULL){ usleep(1); }
if(compare_and_set(&last- continue;
>mNext, next, node)){ }
compare_and_set(&mTail, last, node); compare_and_set(&mTail,last,next);
return; }else{
} T result = next->mValue;
}else{ if(compare_and_set(&mHead, first, next)){
compare_and_set(&mTail, last, next); delete first;
} return result;
} }
} }
}
53. Lock-free Queueかっこいい!
EnqueとDequeの共同作業!
T deque(){
while(1){
const Node* const first = mHead;
void enque(const T& v){ const Node* const last = mTail;
const Node* const node = new Node(v); const Node* const next = first->mNext;
while(1){ if(first != mHead){ continue;}
const Node* const last = mTail; if(first == last){
const Node* const next = last->mNext; if(next == NULL){
if(last != mTail){ continue; } while(mHead->mNext ==
if(next == NULL){ NULL){ usleep(1); }
if(compare_and_set(&last->mNext, next, continue;
node)){ }
compare_and_set(&mTail, last, node); compare_and_set(&mTail,last,next);
return; }else{
} T result = next->mValue;
}else{ if(compare_and_set(&mHead, first, next)){
compare_and_set(&mTail, last, next); delete first;
} return result;
} }
} }
}
65. 楽観的ロック
だめなんです
ロックを2つ取っ
私の安定性…低すぎ… おりゃ!
て
これ消すぞー!
Head Segmentation Fault
これ消すぞー
消したぞー
84. Striped Lock Hashmap
0
1
2
3
4
5
6
7
固定数のLockをmoduloでバケットに割り当てる
スレッドの数が膨大にならない限りロックそのものが
増えても恩恵がないため
バケットごとに対応するロックが縞模様になるので
Striped
87. Lockの無くし方を考える
0
1
2
3
4
5
6
7
線形リスト部分はLock-free Listを使えばいい
バケット部分の拡張が鬼門
94. テーブルの拡張
番兵ノードの間に挟まるアイテムの数が一定数を超
えた場合にテーブルを拡張する
左のテーブルは工夫してあり、基本的に初期化以降
immutable
詳細な部分は論文で
00 02 40 48 6d 7f 80 8a c0 74
0
1
2
3
4
5
6
7
95. テーブルの拡張
1. 拡張操作はテーブルを大きくするだけでおしまい(CASでも可
能)
2. テーブル拡張後は新規探索は新しいテーブル上で行う
3. テーブルが未初期化ならその一個左の番兵ノードから探索
4. 番兵ノードが挿入されているべき個所を見つけ次第、番兵
ノードを挿入する
5. 目的の場所を見つけたら挿入
6. リストが昇順に並んでいるという前提は崩れない
00 02 40 48 60 6d 69 7f 80 8a c0 74
0
1
2
3
4
5
62 6
7
98. Lock-free
java.util.concurrent.ConcurrentHashMap
FAST
性能負けてる!!!
スレッド数
107. Lock-based Skip List
1. ここに挿入したいとする
2. そこ以前のリンクの状態を記憶
3. 昇順にロックを獲得
4. 2の時に記憶した内容と現在の内容が一致していることを確認
5. 全部一致したら下から順にリンクを繋ぎ変えていく
115. 挿入・挿入の衝突
やりなおすだけ
割り込んだ
最下位レベルで割り込まれた場合は初めからやり直す
通常のLock-free Listと同様の作法
116. 挿入・挿入の衝突
割り込んだ
最下位以外で割りこまれたらそこから再開
最下位以外は厳密に一貫している必要はないので
OK
117. 挿入・削除の衝突
削除開始
削除はLock-free Listと同じくマーキングによってCASが失
敗する仕組みになっている
そしてやり直すだけ
120. みんな大好きBtree
18
16 28
5 7 13 16 22 25 33
HashMapと並ぶデータベースの基礎技術
O(log n)のアクセス速度で大量のデータに向
く
121. 普通のBtreeの挿入操作
26
18
16 25
28 28
足りない!
5 7 13 16 22 22 25 26 33
Split
ノードに余裕がなくなった時にSplit操作を行う
余裕のないノードしかSplit操作は行われない
削除はノードの中身が半分を割った時にmerge操
作
123. Lock-free化
Root CAS
複製
複製
複製
ここに挿入したい
必要なデータをすべて複製してしまえばいい
124. Lock-free化
Root
Split
必要なデータをすべて複製してしまえばいい
SplitやMergeも全く同様。部分木を丸ごと作り直す
125. Lock-free化
Root 割り込まれたので
割り込んだ別スレッ CAS失敗→初めから
ド やりなおし
必要なデータをすべて複製してしまえばいい
SplitやMergeも全く同様。部分木を丸ごと作り直す
衝突したらそのスレッドは初めからやり直し
すべての操作はRootに対するCASで直列化
132. あなたが本当に欲しかった物
今をときめく男の娘に必要なのは
「堅牢なだけのトイレの鍵ではない
…!」
いくらデータ構造が強固でスケーラブルに
なっても実地で使えなければ意味がない…!
「Composabilityがない」とも言う
コードを使いまわせないのはソフトウェア工学の
世の中のすべてのロックを
敗北 生まれる前に消し去りたい
過去と未来のすべてのロックをこの手で
143. 邪魔が入る場合
Active
A
B欲しい…
Active
B
144. 邪魔が入る場合
Active
Abort
A CAS
最新の値
Active
B
Bを獲得!
145. どう凄いのか
A
複数個所の最新情報
B がCAS1回で変わる
C Commit
Active
Abort
D
E
160. トランザクションナルキーバリューペア
(TKVP)
通常のキーバリューペア(KVP)をトランザ
クションのために拡張したもの
key value
トランザクション
古い値 新しい値 ステータス
oMel1d
Sde93A 8Yu4naplE2
key old new status
u40F...
iLdsa.
..
saFASFD...
activ
value value’
e
161. トランザクショナルキーバリューペ
ア
statusの値によって読み出し方を分岐
commited:Newの値を読み出す
abort:Oldの値を読み出す
active:競合を解決する(後述)
key old new status
activ
value value’ commited
abort
e
162. トランザクショナルキーバリューペア
(TKVP)
キーバリューストアにはこのように保存さ
れる
バ
キー
リュー
Sde93Au40F...
key oMel1diLdsa...
8Yu4naplE2saFAS
FD…
Sde93A
u40F... value old
oMel1d
iLdsa.
..
value’new
8Yu4naplE2 activ status
saFASFD...
e
163. トランザクショナルキーバリューペ
ア
key old new status
value value’ commited
commited
省略して表記
169. 更新操作
TKVPの所有権を奪って Transaction{
oldが「これまでの最新の値」 set(a, 1); // a=1
newが「これからの最新の値」 set(b, 2); // b=2
をそれぞれ指すよう書き換える set(c, 3); // c=3
}
commitedなら CAS commited
a old new aaaa
hoge set(fEe09d, 1);
cas(a,
4 8 1 [old,fEe09d,hoge])
active
初期化時に保存
abortなら CAS abort したステータス
a old new aaaa
hoge set(fEe09d, 1);
cas(a,
4 8 1 [old,fEe09d,hoge])
active
171. トランザクションの概観
ステータスを初期 初期化
化
必要なTKVPを更新 更新
コミット
ステータスをコ
ミットへ no
成功?
yes
終了
176. トランザクショナルキーバリューペ
ア
ステータスの値によって読み出し方を分岐
commited:Newの値を読み出す
abort:Oldの値を読み出す
active:競合を解決する
key old new status
activ
value value’
e
177. 競合する場合を考える
トランザクション
hoge
Transaction{
set(a, 1);
set(b, 2); トランザクション
fuga
Transaction{
set(b, 999);
184. まとめ
Lock-free Stack, Queue, List, Hashmap,
SkipList, BtreeとDynamicSTMのアルゴリズム
を解説
腑に落ちない所は懇親会で
実装の具体例は僕のGithubや懇親会で
ブログ記事で読みたいネタがあったら懇親会で
分散環境に応用しようと取り組んでます