lockfree list

Post on 22-Jun-2015

4.003 Views

Category:

Documents

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Lockfree List

Lockfree Listって?

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

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

挿入処理

A B C

D

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

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

挿入処理

A B C

D

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

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

CAS

挿入処理

A B CD

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

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

成功

挿入処理

A B C

D

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

CAS

ECAS

挿入処理

A B C

D

片方が必ず失敗する

失敗

E成功

挿入処理

A B C

D

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

E

失敗したのでやりなおし

挿入処理

A B C

D

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

E

CAS

挿入処理

A B CD

これで直列化できる

E

成功

削除処理

A B C

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

削除

削除処理

A B C

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

CAS

削除処理

A C

B

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

delete

削除処理

A C

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

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

A B C D

削除 削除

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

A B C D

CAS CAS

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

A

B

C

D

delete

delete

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

A

D

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

A B C D

E

削除

挿入

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

A B C D

E

CAS

CAS

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

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

A

B

C D

E

delete

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

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

A C D

E

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

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

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

そうなると?

A B C D

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

そうなると?

A B C D

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

そうなると?

A B

C

D

α:成功したなら良し

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

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

A B C D

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

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

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

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

A B C D

α: CASを試みる

β: CASを試みる

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

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

A

B

C D

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

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

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

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

A C D

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

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

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

A C D

α:削除マークを発見

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

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

A C D

α: CASを試みる

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

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

A

C

D

α: CAS成功

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

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

A D

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

A B C D

E

削除

挿入

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

A B C D

E

α:削除マーキング

β: CASを試みる

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

A B C D

E

α:削除マーキング

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

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

A B C D

E

α: CASを試みる

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

A

B

C D

E

α: CAS成功

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

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

A C D

E

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

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

A C D

E

β: CASを試みる

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

–直列化できる

A C DE

β: CAS成功

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

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

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

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

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

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

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

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

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

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

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

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

ABA問題って?

A B C D

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

ABA問題って?

A B C D

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

ABA問題って?

A B C D

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

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

ABA問題って?

A B C D

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

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

ABA問題って?

A B

C

D

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

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

ABA問題って?

A B

C

D

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

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

ABA問題って?

A B X D

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

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

ABA問題って?

A B X D

α:やっと目覚める

ABA問題って?

A B X D

α: Cの削除を再開する

ABA問題って?

A B X D

α: CASを発行

CAS

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

ている

ABA問題って?

A B

X

D

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

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

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

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

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

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

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

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

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

top related