lockfree list

58
Lockfree List

Upload: kumazaki-hiroki

Post on 22-Jun-2015

4.003 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Lockfree list

Lockfree List

Page 2: Lockfree list

Lockfree Listって?

• 複数のスレッドから同時に挿入・検索・削除を行う事が可能な並行線形リストをLockfreeにしたもの

• 挿入・削除が立て込んでいてもロックを用いないので検索がロックされない

Page 3: Lockfree list

挿入処理

A B C

D

リストの繋ぎ替え処理が一瞬で片付くように工夫して、検索処理の邪魔をしない

CompareAndSwap(以下 CAS)でポインタを差し替える

Page 4: Lockfree list

挿入処理

A B C

D

リストの繋ぎ替え処理が一瞬で片付くように工夫して、検索処理の邪魔をしない

CompareAndSwap(以下 CAS)でポインタを差し替える

CAS

Page 5: Lockfree list

挿入処理

A B CD

リストの繋ぎ替え処理が一瞬で片付くように工夫して、検索処理の邪魔をしない

CompareAndSwap(以下 CAS)でポインタを差し替える

成功

Page 6: Lockfree list

挿入処理

A B C

D

CASを使う事によって、同一の場所に同時に複数の挿入が発生しても

CAS

ECAS

Page 7: Lockfree list

挿入処理

A B C

D

片方が必ず失敗する

失敗

E成功

Page 8: Lockfree list

挿入処理

A B C

D

失敗したらもう一度接続先を改めてやりなおす

E

失敗したのでやりなおし

Page 9: Lockfree list

挿入処理

A B C

D

失敗したらもう一度接続先を改めてやりなおす

E

CAS

Page 10: Lockfree list

挿入処理

A B CD

これで直列化できる

E

成功

Page 11: Lockfree list

削除処理

A B C

挿入処理と同様に、ポインタを CASで繋ぎ変える

削除

Page 12: Lockfree list

削除処理

A B C

挿入処理と同様に、ポインタを CASで繋ぎ変える

CAS

Page 13: Lockfree list

削除処理

A C

B

こうして追い出した後に Bを delete- CASのおかげで、複数のスレッドが一つのノードを取り合っても複数回 deleteせずに済む

delete

Page 14: Lockfree list

削除処理

A C

こうして追い出した後に Bを delete- CASのおかげで、複数のスレッドが一つのノードを取り合っても複数回 deleteせずに済む

Page 15: Lockfree list

しかし問題が• Bと Cを同時に削除しようとするとデータ構造が破壊される

A B C D

削除 削除

Page 16: Lockfree list

しかし問題が• Bと Cを同時に削除しようとするとデータ構造が破壊される

A B C D

CAS CAS

Page 17: Lockfree list

しかし問題が• Bと Cを同時に削除しようとするとデータ構造が破壊される–削除したはずの Cに接続されてしまう

A

B

C

D

delete

delete

Page 18: Lockfree list

しかし問題が• Bと Cを同時に削除しようとするとデータ構造が破壊される–削除したはずの Cに接続されてしまう–こちらを状況 1と呼ぶことにします

A

D

Page 19: Lockfree list

しかし問題が• 削除されるノードの次に挿入する際も

A B C D

E

削除

挿入

Page 20: Lockfree list

しかし問題が• 削除されるノードの次に挿入する際も

A B C D

E

CAS

CAS

Page 21: Lockfree list

しかし問題が• 削除されるノードの次に挿入する際も

–挿入されたはずの物が孤立してしまう

A

B

C D

E

delete

Page 22: Lockfree list

しかし問題が• 削除されるノードの次に挿入する際も

–挿入されたはずの物が孤立してしまう–こちらを状況 2と呼ぶことにします

A C D

E

Page 23: Lockfree list

そこで削除を2段階操作とする• ポインタに削除マークを取り付け、削除操作をマーキング・削除の 2ステップに分割する

• 削除マークとポインタは一つの CASで同時に扱う事ができるとする–動的に確保したオブジェクトは大体 4byte程度でアラインされているのでポインタの下位bitがそのままフラグとして使える

• リストを辿るスレッドは、マークされたノードを発見したらそれを削除する

Page 24: Lockfree list

そうなると?

A B C D

α:ここまでイタレーションし削除マーキング

Page 25: Lockfree list

そうなると?

A B C D

α:そして CASによる削除を試みるCAS

Page 26: Lockfree list

そうなると?

A B

C

D

α:成功したなら良し

Page 27: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして

A B C D

α:ここまでイタレーションし削除マーキング

β:ここまでイタレーションし削除マーキング

Page 28: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして

A B C D

α: CASを試みる

β: CASを試みる

Page 29: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして

A

B

C D

α: Bのポインタがマーキングされているので CASに失敗する

β: Aのポインタは変わらないので CASに成功する

Page 30: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして

A C D

α:リストの先頭からイタレーションし直す

Page 31: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして

A C D

α:削除マークを発見

Page 32: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして

A C D

α: CASを試みる

Page 33: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして• 直列化できる

A

C

D

α: CAS成功

Page 34: Lockfree list

そうなると?• 先ほどの状況 1は

• α ・ βの 2スレッドで同時に削除するとして• 直列化できる

A D

Page 35: Lockfree list

そうなると?• 先ほどの状況 2は

A B C D

E

削除

挿入

Page 36: Lockfree list

そうなると?• 先ほどの状況 2は

A B C D

E

α:削除マーキング

β: CASを試みる

Page 37: Lockfree list

そうなると?• 先ほどの状況 2は

A B C D

E

α:削除マーキング

β:マークのせいで CAS失敗

Page 38: Lockfree list

そうなると?• 先ほどの状況 2は

A B C D

E

α: CASを試みる

Page 39: Lockfree list

そうなると?• 先ほどの状況 2は

A

B

C D

E

α: CAS成功

β:リストの頭から再びイタレート

Page 40: Lockfree list

そうなると?• 先ほどの状況 2は

A C D

E

β:リストの頭から再びイタレート

Page 41: Lockfree list

そうなると?• 先ほどの状況 2は

A C D

E

β: CASを試みる

Page 42: Lockfree list

そうなると?• 先ほどの状況 2は

–直列化できる

A C DE

β: CAS成功

Page 43: Lockfree list

ポイント• リストをイタレートするときは、削除マークが付いていないことを常に確認する–付いているならその場で削除させる

• これにより削除済みのオブジェクトに対して操作をしてしまう状況を防げる

–付いていない事を確認したはずの物にいつの間にか削除マークが付いていたならイタレートを頭からやり直す

Page 44: Lockfree list

問題点• イテレータがしょっちゅうリストの先頭に戻ってしまうので– コストが高い。なのでイタレートを進める度にロックを繰り返す悲観的ロックリストや、書き換えを行う時だけロックを行い、ロックに失敗したらリストの先頭に戻る楽観的ロックリストなどを状況に応じて使い分ける

• 読み出し頻度が多い程、ロック粒度を細かくするほうが良い

– std::list<hoge>::iterator it;のような形として個々のスレッドにイテレータを持たせるのは無理

– そもそも STLの list::iteratorも並列利用は無保証– std::setのように挿入 検索・削除の利用のみ・

Page 45: Lockfree list

話題• このリストでは ABA問題には未対処

– 挿入・削除が運悪く重なって、望まない状況で CASが成功してしまう場合がある

• 対処方法は2種類ある– 運悪く同じアドレスに CASすることになっても CASが成功しないよう、ポインタに更新スタンプを付ける

• スタンプが運悪く一周してしまうとやはり ABA問題– 参照している最中のオブジェクトは削除しない事にする

• 参照カウンタ? →  atomicカウンタ重いです…• ガベージコレクタ? → マルチスレッド対応のGCが必要• ハザードポインタ → 当店お勧め

Page 46: Lockfree list

ABA問題って?

A B C D

α:ここまでイタレーションし削除マーキング

Page 47: Lockfree list

ABA問題って?

A B C D

α:そのまましばらく休眠

Page 48: Lockfree list

ABA問題って?

A B C D

α:そのまましばらく休眠

β:別の用事でイタレーションしてくる

Page 49: Lockfree list

ABA問題って?

A B C D

α:そのまましばらく休眠

β:マークを確認したので削除

Page 50: Lockfree list

ABA問題って?

A B

C

D

α:そのまましばらく休眠

β:マークを確認したので削除

Page 51: Lockfree list

ABA問題って?

A B

C

D

α:そのまましばらく休眠

γ: Bの後に新規ノード Xを挿入する

Page 52: Lockfree list

ABA問題って?

A B X D

α:そのまましばらく休眠

γ:挿入時に運悪く αが参照中のノードを使いまわしてしまう

Page 53: Lockfree list

ABA問題って?

A B X D

α:やっと目覚める

Page 54: Lockfree list

ABA問題って?

A B X D

α: Cの削除を再開する

Page 55: Lockfree list

ABA問題って?

A B X D

α: CASを発行

CAS

Cが保存されていた時と同じポインタを指してしまっ

ている

Page 56: Lockfree list

ABA問題って?

A B

X

D

α:アドレスが一致しているので CAS成功

削除する気の無かった Xが削除されてしまう

Page 57: Lockfree list

ABA問題って?• ここに書いた状況以外にも、アドレスを使いまわす限り、意図しないアドレス一致の危険性は付いて回る

• 更新カウンタを付ければノードが使い回された後でもカウンタの値を見る事で不一致を検出できるため問題を回避できるが、一度に CASしなくてはならないビット数が増えるため、 DCAS命令や STMが必要になる– 更新カウンタに割くビット数をケチるとカウンタが一巡して一致する危険性がある

Page 58: Lockfree list

ABA問題って?• そもそも他のスレッドが参照している最中のものを削除して使いまわすから悪い

• じゃあ削除しなければ良い。でもどうやって?

• 参照カウンタ→カウンタを atomicに操作する必要がある上、ノードごとにカウンタが付くためリストが肥大化

• ガベージコレクタ→ GC中に全スレッドを止めるしか安全な方法が無い

• そこでハザードポインタです(次回へ