Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

よくわかるHopscotch hashing

5,112 views

Published on

  • Be the first to comment

よくわかるHopscotch hashing

  1. 1. よくわかるHopscotch Hashing @kumagi
  2. 2. Locking!!!! このアルゴリズムはLock-freeではありません! Lock-freeを期待して見にきた方は回れ右!
  3. 3. Hashmapとは?• みんな大好き連想配列! – RubyでもPerlでもPythonでも人気者! – C++でならunordered_map、JavaならHashMap• 何かの値をキーとして別の値を保存・検索できるデータ構造 – 「山!」→「川!」• とにかく高速! – O(1)で動作するデータ構造 • 大量のデータを入れても検索速度が変わらないただ一つのデータ 構造
  4. 4. その仕組み山! 8番目! 川! Hash関数に通すだけで保存or検索すべき場所が判明する!
  5. 5. 欠点混んでくるとぶつかる
  6. 6. ぶつかったら? 広々!大きなところへ引っ越せばいい
  7. 7. でもそれは最後の手段• Hash値が衝突するのはそんなに珍しくない – そのために配列確保しなおしてたら遅すぎる – メモリ効率悪すぎる• メモリにやさしい解決方法が望まれている
  8. 8. その戦略• 大きく分けて二つ Open Closed Addressing Addressing or
  9. 9. Closed Addressing新しく作ってポインタで繋ぐ!
  10. 10. Closed Addressing• 1つのアドレスにつき1つの場所(とそこからポインタで繋 がった場所)にしか対象の物が置かれないからClosedと呼ば れる• ポインタで繋がるリスト構造から「チェインハッシュ」とも 呼ばれる• 利点 – チェインの長さにだけ気をつければO(1)の速度を保てる – ポインタを繋ぎ換えればLinearHashなどが作れるし簡単• 欠点 – ポインタを辿るのが遅い
  11. 11. 対するOpen Addressingは…
  12. 12. Open Addressing やったね! 隣を使う!
  13. 13. 選べる探索バリエーション(併用不 可) Liner Probing Quadratic Probing ±1, 2, 4, 8, 16… Double Hashing ±Hash(x)
  14. 14. Open Addressing• 一つのHash値に対して配列上の複数の場所が該当しうるの でOpenと名がつくんだと思う• 利点 – キャッシュの局所性を生かせるので速い(特にLinear Probing) – ポインタの分だけ省メモリ• 欠点 – 削除時にはデータを消さずに削除フラグを立てて再利用させる だけ • データが存在している事そのものが「まだ配列の続きにあるかも よ」という状態を表しているので完全に消しちゃうとマズい • よって挿入・削除で密度が上がってくると入ってるデータが少な い時でも検索・挿入・削除の性能がガタ落ちする
  15. 15. ベンチマーク比較 キ ャ ッ シ ュ ミ ス 頻 度 使用済み要素の割合
  16. 16. Cuckoo Hashing• 鳥のカッコウの習性のように、托卵する際に他の鳥の卵が あった場合に退けるハッシュマップ – OpenAddressingにもClosedAddressingにも分類しがたい• 2つの配列と2つのハッシュ関数を用意する• きちんと作れば頑健で高信頼 – 詳しくはクヌース先生のTAoCP本を。
  17. 17. Cuckoo Hashing• 2つの配列に2つのハッシュ関数。1つのアイテムは2つの配列 のうちどちらかに入っていれば良い。• 衝突時には もう一つの配列を使う Hash1(y) Hash1(x)• 両方埋まってたら既存のをもう一つの配列へ Hash2(x) Hash2(y)• それも埋まってたらその片方をもう一つの配列へ(以後再帰的に全部• 検索はいつも2つの配列を2つのハッシュ関数で探すだけ
  18. 18. • 利点 Cuckoo Hashing – 検索は2つの配列の一箇所ずつを探すだけなので高速 – 多少ハッシュ値が偏ってももう一つのハッシュ値が散れば問題 ない• 欠点 – 密度に応じて挿入のコストが上がる – 2つの配列を使う分メモリを食う • チェインのポインタを持たなくてよいのでそんなに問題でないか も – 特性上、全体の半分が埋まったところで性能がガタ落ちする • タチが悪いと円環して終わらない事もあり得るのかな – つまり半分埋まる前に拡大する必要がある • スッカスカのテーブルを維持しなきゃいけないからありがたみ少 ない
  19. 19. そこでHopscotch!OpenAddressingのLinearProbingのキャッシュヒット 力とOpenAddressingの検索力を両立する 新しいOpenAddressingなハッシュテーブル!
  20. 20. What is Hopscotch? いわゆる「けんけんぱ」 飛び方を決めてその通りに 飛ぶ遊び 名前かっこいい!
  21. 21. データ構造• 配列上の全バケットがデータの他にHop情報を持つ。 – Hop情報は物理的には1ワード幅のビット列 • 図では大きさの都合上8bitということに – ここから隣のどのバケットに、本来ここに入るべきだったアイ テムが置かれているかを示す。 1word アイテム 11001010 1なら対応データ有り 0なら
  22. 22. 検索• 普通のHashmapと同様にバケットを検索する• そのバケットのHop情報を見て、このバケット位置に対応する データを順番に調べていき目的のものを見つける• 1wordのbit数分の比較を行えば検索の成功or失敗を決定できる – 比較回数はO(1)で済む アイテム 11001010 12 3 4 4つbitが立っていたので比較4 回
  23. 23. 検索• チェインハッシュで言うところのこれと状態は同じ – 一つのバケット位置の下に複数のアイテムがぶら下がる – 配列に連続している分、左のHopscotchのほうがキャッシュ ヒット率が高い 1 アイテム アイテム 11001010 2 アイテム 3 アイテム 12 3 4 4 アイテム
  24. 24. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 無いならテーブルを拡張して全部再配置してやり直し アイテム 11010100 アイテム 11001010ここに入れたいけ ど空きが無い ビッシ リ
  25. 25. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 空バケットとはHop情報部分はともかくアイテム部分が空の物 8word bit先まで空きを探 すここに入れたいけ ど空きが無い ビッシ リ
  26. 26. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 空バケットとはHop情報部分はともかくアイテム部分が空の物 空バケットあっ た! 8word bit先まで空きを探 すここに入れたいけ ど空きが無い ビッシ リ
  27. 27. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 空バケットとはHop情報部分はともかくアイテム部分が空の物• 空バケットをswapしながら左に移動させていく – バケットからHop情報で飛べる1wordビット範囲でしかswapは しない アイテム 01010100ここに入れたいけ ど空きが無い ビッシ リ swap
  28. 28. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 空バケットとはHop情報部分はともかくアイテム部分が空の物• 空バケットをswapしながら左に移動させていく – バケットからHop情報で飛べる1wordビット範囲でしかswapは しない アイテム 00010110ここに入れたいけ ど空きが無い ビッシ リ swap完了
  29. 29. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 空バケットとはHop情報部分はともかくアイテム部分が空の物• 空バケットをswapしながら左に移動させていく – バケットからHop情報で飛べる1wordビット範囲でしかswapは しない• 挿入したい位置から1word幅になるまで続ける ここに入れたいけ ど空きが無い ビッシ リ swap
  30. 30. 挿入• およそ8ワード分のビット数先のバケットまで配列を舐めな がら、アイテムが入っていない空バケットが無いかを探す – 空バケットとはHop情報部分はともかくアイテム部分が空の物• 空バケットをswapしながら左に移動させていく – バケットからHop情報で飛べる1wordビット範囲でしかswapは しない• 挿入したい位置から1word幅になるまで続ける ここに入れたいけ ど空きが無い ビッシ リ swap完了
  31. 31. 挿入• これで空き部分に挿入できる
  32. 32. 挿入• これで空き部分に挿入できる• アイテムを書き込んでHop情報を更新して完了 アイテム 11001011
  33. 33. 削除• 普通に検索して該当するアイテムを消去してHop情報のbitを落と すだけ アイテム 11001010
  34. 34. 削除• 普通に検索して該当するアイテムを消去してHop情報のbitを落と すだけ – 超簡単 アイテム 11000010
  35. 35. ポイント• バケットそれぞれにアイテムとHop情報が入っている。 – Hash値を算出して配列の該当部分にアクセスした瞬間に、その 周辺もキャッシュラインに同時に載るためキャッシュミスが減 る• 検索時は必ずそのバケットから1wordのbit数以内の距離にあ る – カッコウハッシュはバケット2つしか1つのアイテムが入りうる 候補が無かったのに対して、1word幅bit個まで候補が増えてい る • 配列を拡張しなくても入る量が多い• そんなわけで実装してみた。Boost::unordered_mapより速 い。 – https://gist.github.com/2943289

×