Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Red-Black Tree for Ruby
Kyuden Masahiro
March 22, 2019
Programming
1
1.1k
Red-Black Tree for Ruby
https://railsdm.github.io/
Kyuden Masahiro
March 22, 2019
Tweet
Share
More Decks by Kyuden Masahiro
See All by Kyuden Masahiro
365日24時間稼働必須サービスの 完全無停止DB移行
kyuden
23
9.6k
Why Rails 5.1
kyuden
1
570
Rails Authorization
kyuden
21
13k
One Night Vue.js
kyuden
14
3.3k
Other Decks in Programming
See All in Programming
LIFFで動く割り勘アプリTATEKAをリリースしてみた話
inoue2002
0
260
TokyoR#103_DataProcessing
kilometer
0
540
コンピュータビジョンセミナー2 / computer_vision_seminar_libSGM
fixstars
0
320
ITエンジニア特化型Q&Aサイトteratailを 言語、DB、クラウドなど フルリプレイスした話
leveragestech
0
440
エンジニア向け会社紹介資料/engineer-recruiting-pitch
xmile
PRO
0
110
PHPアプリケーションにおけるアーキテクチャメトリクスについて / Architecture Metrics in PHP Applications
isanasan
1
280
Refactor with using `available` and `deprecated`
417_72ki
3
380
PHPDocにおける配列の型定義を少し知る
shimabox
1
140
Micro Frontends with Module Federation @MicroFrontend Summit 2023
manfredsteyer
PRO
0
620
(新米)エンジニアリングマネージャーのしごと #RSGT2023
murabayashi
9
5.9k
OIDC仕様に準拠した Makuake ID連携基盤構築の裏側
ymtdzzz
0
580
Showkase、Paparazziを用いたビジュアルリグレッションテストの導入にチャレンジした話 / MoT TechTalk #15
mot_techtalk
0
120
Featured
See All Featured
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
31
20k
The Invisible Customer
myddelton
113
12k
We Have a Design System, Now What?
morganepeng
37
5.9k
What's new in Ruby 2.0
geeforr
336
30k
Building Your Own Lightsaber
phodgson
96
4.9k
Fantastic passwords and where to find them - at NoRuKo
philnash
32
1.9k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
29
7.9k
Become a Pro
speakerdeck
PRO
6
3.2k
The Language of Interfaces
destraynor
149
21k
Thoughts on Productivity
jonyablonski
49
2.7k
Fashionably flexible responsive web design (full day workshop)
malarkey
396
63k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
15
1.2k
Transcript
Red-Black Tree for Ruby Kyuden Masahiro
I’m kyuden • Github: kyuden • Twitter: @kyuden_ • Banken
gem creator • https://github.com/kyuden/banken • Sorcery gem commiter • https://github.com/Sorcery/sorcery • WEB+DB Press Ruby連載(vol96~101)
None
None
Ruby biz Grand prix 2017
Our Team
RubyKaigi 2017
RubyKaigi 2017 RubyKaigi 2018
今回のストーリーの始まり • インメモリで約800万個の数値(integerのみ)の集合から最小 値を取得したい – 数値は頻繁に追加/削除され集合の数は増減する – Rubyを使用する(Redisは使用しないものとする) ※ 実際はもう少し複雑な要件だったがミニマルにして抜粋
どう実装しましょうか?
SortedSet • Rubyの標準ライブラリであるSortedSetを発見 • SortedSetという名前から今回の用途にぴったりだと確信
これで無事何もなければこのスライドは無価値
問題発生 • 想像以上にパフォーマンスがでなかった ※1 – まず自分が書いたプログラムのバグを疑う • 見当たらない • プロファイリングしてみる
– SortedSetに時間がかかりすぎていた ※1 ベンチマークはのちほど
SortedSetの内部実装 • rbtreeというgemがrequireできる場合は内部記憶として rbtreeを利用する • ただrbtreeはthird partyのgemでありデフォルトではイン ストールされていない – その場合requireできないので内部記憶はSet同様Hash
を利用する – なのでデフォルトの挙動はこちら
SortedSetの内部実装 – rbtreeを利用しない場合 - • SourtedSet#first – SourtedSet#firstが呼ばれると全要素をArray#sort!して いた •
一度sortしたものはキャッシュするが新しい要素が 追加/削除された場合はキャッシュが消える • 最悪計算量はO(n^2) • SortedSetという名前からO(log n)を期待していた
各計算量のおけるステップ数比較 log n n n log n n^2 2 5
12 25 3 10 33 100 4 15 59 225 4 20 86 400 5 25 116 625 5 30 147 900 7 100 664 10000 8 300 2468 90000 10 1000 9966 1000000 13 10000 132877 100000000 16 100000 1660964 10000000000 20 1000000 19931568 1000000000000
解決? • とりあえず`gem install rbtree`すればO(log n)が手に入るので今回の問題 は解決する – だがRubyとしてこれで良いのかは少し疑問が残る –
気づかずにO(n^2)のSortedSetを利用してしまう人がいるのではないか – Ruby本体にrbtreeのようなデータ構造の実装が入ると今回の自分のよ うに調査しなくて済むし、気づかなかった人もO(log n)が手に入る – もしくはRuby本体からSortedSetを削除し後方互換性を考えgemとし て提供するのも選択肢としてはあり?
SortedSet#first vs SortedSet#first with rbtree Ruby 2.6.1 gcc version 8.1.0
(Ubuntu 8.1.0-5ubuntu1~16.04)
Rbtreeとは?
Rbtree • Red-Black Tree(赤黒木)の実装を提供するC extension gem Red-Black Tree • 平衡二分探索木の一種であり現在も広く利用されているデータ構造
– std::set, std::map (GNU C++ ) – TreeMap, TreeSet (Java) – SortedSet (C# .NET) – etc
Red-Black Tree -性質- • 2分木 • 各ノードは赤か黒の色を持つ • 根は黒ノード •
赤ノードの子は黒ノード • 全ての葉から根への経路上の 黒ノードは同じ個数 1 2 5 4 6 7 8 3
Red-Black Tree -性能- • 探索、挿入、削除の最悪計算量がO(log n) • 木の高さが2logn以下 – 最短のパスは全てが黒ノードのパス、最長のパスは赤黒交互に並ぶノードのパス
• 最短の最長のパスの長さは2倍以内に収まる ※ AVL木も探索、挿入、削除の最悪計算量はO(log n)だが赤黒木より厳密な平衡性を保つので 赤黒木より木高さも低く検索は早い。一方、その分挿入、削除のリバランスに時間がかかる ため赤黒木より遅くなる場合がある。 よって頻繁に更新しない辞書のような用途だとAVL木が適しているが、様々な汎用的な用 途を考慮すると赤黒木のほうが適しているケースが多いため様々な言語のライブラリの実 装として選択されているのではないかと推測する
Red-Black Tree -ユースケース-優先度付きキュー- • Linuxカーネル 2.6.23 にマージされたCompletely Fair Scheduler (CFS)も内
部でも仮想実行時間でキーとし優先順序付けされた赤黒木を使用しタスク を選択している。 https://github.com/torvalds/linux/blob/master/kernel/sched/fair.c
Red-Black Tree -ユースケース-転置インデックス検索 • Rbtree#lower_bound/upper_bound/boundを使用すれば、キーを正確に指 定する必要はなく、次の大きいキーや次に小さいキー、有る範囲のキーにあ る要素をO(log n)で取得できる – Hashはキー順に並んでいないので同様の操作を行う場合、全てのキーを
参照する必要がある
実装が複雑 Red-Black Treeの優れた性質の代償
Left-leaning Red-Black Tree
Left-leaning Red-Black Tree • 2008年にRobert Sedgewickによって発表された論文※1にて発表された赤黒 木の異種(variant) • もともと赤黒木は1978年にRobert SedgewickとLeonidas
J. Guibasによって 発表された※2ことを考えると、相対的には最近発表されたといえ、かつ Robert Sedgewick自身による赤黒木の再発明ということでわくわくする ※1 Robert Sedgewick. Left-leaning Red–Black Trees(2008) http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf ※2 赤黒木の元になったのは1972年のRudolf Bayerの発明によるもの
実装が複雑 Red-Black Treeの優れた性質の代償
Left-leaning Red-Black Tree - 要約 - • Left-leaning Red-Black Tree(以後
左傾赤黒木と記載する)は赤黒木より非常にシンプル に実装でき、挿入、削除においては1/4以下のコード量で実装できる – よって従来の赤黒木の複雑性を解決し、メンテナンス性が向上する • 赤黒木と左傾赤黒木のパフォーマンス比較に関しては明確にはこの論文で述べられて いない – Robert Sedgewickによるプレゼンテーション資料※1では左傾赤黒木の特徴として 赤黒木と比較して”less code implies faster insert, delete”とパフォーマンスに関し 言及されていた。(だがあくまで”imply”であり計測結果の数値の記載はない) ※1 Robert Sedgewick. Left-leaning Red–Black Trees http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf
Left-leaning Red-Black Tree - コンセプト - • 左傾赤黒木は赤黒木に対してさらに制約を追加することで赤黒木の複 雑な条件分岐を減らすことに成功している –
赤黒木を2-3-4木もしくは2-3木に1対1対応させる過程で新しい制 約が生まれ結果それが左傾赤黒木となる 1 2 5 4 6 7 8 3 1 2 3 5 4 6 7 8
2-3-4木 -ノードの種類 - b • 2ノードは、1つの要素と2つの子をもつ • 3ノードは、2つの要素と3つの子をもつ • 4ノードは、3つの要素と4つの子をもつ
※子は空の場合もあり a c b d a c e b d f a c e g
2-3-4木をRed-Black Treeで表現 -3ノード場合- 5 3 3 5 • 子ノードに赤ノードを含む赤黒木で表現する –
3ノードの場合は上記のように2種類の赤黒木に分類できる – 必ず左の子に赤ノードが傾くという制約を加え、もし右の子に赤いノードが傾いている場合は 左回転(Left flip)して左の子に赤ノードがくるよう強制する(Left-leaning Red-Black Treeという命 名はこの制約からきたものである) • その結果、右の子が赤ノードである多くのケースを排除でき赤黒木の分岐条件を減らすこと ができる 3 3 5 5 3 3
2-3-4木をRed-Black Treeで表現 -2ノード場合- 4 • 2ノードの場合は2分木なのでそのまま赤黒木で表現できる 4
2-3-4木をRed-Black Treeで表現 -4ノード場合- 8 7 6 7 8 6 7
3 6 8 6 8 7 8 6 7 6 7 8 6 8 7
Left-leaning Red-Black Treeを2-3-4木で表現 • 左傾赤黒木が2-3-4木にみえてくるはず 1 2 5 4 6
7 8 3
Left-leaning Red-Black Treeを2-3-4木で表現 • 左傾赤黒木が2-3-4木にみえてくるはず.. 1 2 5 4 6
7 8 3
Left-leaning Red-Black Treeを2-3-4木で表現 • 左傾赤黒木が2-3-4木にみえてくるはず.... 1 2 5 4 6
7 8 3
Left-leaning Red-Black Treeを2-3-4木で表現 • 左傾赤黒木が2-3-4木にみえてくるはず...... 1 2 5 4 6
7 8 3 6 7 8 3 5 1 2 4
Left-leaning Red-Black Treeを2-3-4木で表現 • 左傾赤黒木が2-3-4木にみえてくるはず! 1 2 3 5 4
6 7 8
Left-leaning Red-Black Tree – ここまでのまとめ- • 赤黒木は赤黒ノードのノードパターンを全てチェックする必 要があったが、2-3-4木を赤黒木で表現する過程で必要となる 制約を加えることで、いくつかのパターンが自明となり チェックする必要なくなったため左傾赤黒木はシンプルな実
装となる • ただ、4ノードの分割処理など他にもケアする内容があり実 際はもう少し複雑だが、本資料では詳細な左傾赤黒木の解説 は割愛する
https://github.com/kyuden/llrbtree
kyuden/llrbtree • 左傾赤黒木を提供するC extention gem • 左傾赤黒木は`llrbtree/ext/llrbtree/tree.c`に実装 • binding部分は左傾赤黒木に合わせて改変したが基本的には rbtree
gemと同じであるためbinding部分のCopyrightは原 作者のまま • Robert Sedgewickがさきほどの論文で推奨していた再帰に よる追加、削除処理の実装を選択
kyuden/llrbtree -モチベーション- • 論文には明示的に赤黒木と左傾赤黒木のパフォーマンス比較が記載 されていなかった • 論文には左傾赤黒木のデメリットの記載が少なかった • もし、前述した論文やプレゼンテーション資料通りコード量も少な くメンテナブルで、かつ赤黒木より高速にうまく実装できれ
ば、Rubyにfeature request送ってみるのもありだと思った – 少なくとも現在のSortedSet#firstより数千倍は高速化できる • 論文読んで高まった
llrbtreeとrbtreeのパフォーマンス比較 -検索- Ruby 2.6.1 gcc version 8.1.0 (Ubuntu 8.1.0-5ubuntu1~16.04)
llrbtreeとrbtreeのパフォーマンス比較 -挿入- Ruby 2.6.1 gcc version 8.1.0 (Ubuntu 8.1.0-5ubuntu1~16.04) Ruby
2.6.1 gcc version 8.1.0 (Ubuntu 8.1.0-5ubuntu1~16.04)
llrbtreeとrbtreeのパフォーマンス比較 -削除- Ruby 2.6.1 gcc version 8.1.0 (Ubuntu 8.1.0-5ubuntu1~16.04)
パフォーマンス結果 -要約- • 左傾赤黒木は検索、挿入、削除がいずれの操作においても赤黒木より遅い という結果となった – 削除に関しては要素数が100万個の場合約2倍の開きがあった – 左傾赤黒木は赤黒木に比べて挿入と削除、各操作における回転数が挿入 は2.96x、削除は51.99x増えている※1
• 前述したRobert Sedgewickの論文にパフォーマンス比較がなかったのは左 傾赤黒木が遅いため? ※1 http://www.read.seas.harvard.edu/~kohler/notes/llrb.html
Red-Black Tree vs Left-leaning Red-Black Tree • 左傾赤黒木はたしかにコード量はシンプルだがパフォーマンス比較の 結果をふまえると、例えばプログラミング言語の標準ライブラリとし て提供するなら赤黒木の方がメリットがあるのではないか
– 赤黒木の方が歴史が長く安定して実用されてきたという実績から も左傾赤黒木が選ばれるユースケースは限られてくるのではない か – もし要素数が少ない場合のみの使用を前提とするアプリケーショ ンなら実装コストを優先して左傾赤黒木が選択肢になりえる
本日の内容 • SortedSetの内部実装/気をつけるべき点 • Red-Black Tree – 性質/性能 – ユースケース
• Left-leaning Red-Black Tree – コンセプト – Red-Black Treeとのパフォーマンス比較
おわり