web-dev-qa-db-ja.com

BツリーはAVLやRedBlack-Treeよりも高速ですか?

パフォーマンスは決して白黒ではないことを知っています。多くの場合、1つの実装はXの場合より速く、Yの場合は遅いなどです。しかし、一般的に-BツリーはAVLやRedBlack-Treesよりも速いですか?それらはAVLツリー(そしておそらくRedBlack-treesも)よりも実装がかなり複雑ですが、faster(その複雑さは報われますか)?

編集:また、それらがより高速であれば、同等のAVL/RedBlackツリー(ノード/コンテンツに関して)-whyより高速ですか?

64
thr

Seanの投稿(現在受け入れられている投稿)には、いくつかの誤った主張が含まれています。申し訳ありませんが、ショーン、失礼なつもりはありません。私の声明が事実に基づいていることを納得していただければ幸いです。

ユースケースはまったく異なるため、比較することはできません。

どちらも、完全に順序付けされたアイテムのセットを、高速なルックアップ、挿入、削除で維持するために使用されます。それらは同じインターフェースと同じ意図を持っています。

RBツリーは通常、高速アクセスを提供するために使用されるメモリ内構造です(理想的には、データへのO(logN))。..]

alwaysO(log n)

Bツリーは通常、ディスクベースの構造であるため、インメモリデータよりも本質的に低速です。

ナンセンス。検索ツリーをディスクに保存する場合、通常はBツリーを使用します。それは本当です。ディスクにデータを保存すると、メモリ内のデータよりもアクセスが遅くなります。しかし、ディスクに保存されている赤黒ツリーは、メモリに保存されている赤黒ツリーよりもalso遅いです。

ここでリンゴとオレンジを比較しています。本当に興味深いのは、インメモリBツリーとインメモリ赤黒ツリーの比較です。

[余談ですが、赤黒のツリーとは対照的に、BツリーはI/Oモデルで理論的に効率的です。ソート用のI/Oモデルを実験的にテスト(および検証)しました。 Bツリーでも同様に機能すると期待しています。]

Bツリーがバイナリツリーになることはめったにありません。ノードが持つことのできる子の数は通常、多数です。

明確にするために、Bツリーノードのサイズ範囲はツリーのパラメーターです(C++では、テンプレートパラメーターとして整数値を使用できます)。

データが変更されると、Bツリー構造の管理は非常に複雑になる可能性があります。

赤黒の木よりも理解(および実装)がはるかに簡単であることを覚えています。

Bツリーは、ディスクアクセスの数を最小限に抑えて、データの取得が合理的に決定されるようにします。

それは本当です。

非常にデータベース内のデータの一部を検索するために必要な4つのBツリーアクセスなどが表示されることは珍しくありません。

データを取得しましたか?

ほとんどの場合、インメモリRBツリーの方が高速だと思います。

データを取得しましたか?

ルックアップはバイナリであるため、何かを見つけるのは非常に簡単です。 Bツリーはノードごとに複数の子を持つことができるため、各ノードで適切な子を探すためにノードをスキャンする必要があります。これはO(N)操作です。

各ノードのサイズは固定パラメーターであるため、線形スキャンを実行しても、O(1)になります。各ノードのサイズを大きくした場合、通常は配列をソートしたままにして、O(log n)になるようにします。

RBツリーでは、1つの比較を行ってから分岐しているため、O(logN)となります。

あなたはリンゴとオレンジを比較しています。 O(log n)は、Bツリーの場合と同様に、ツリーの高さが最大でO(log n)であるためです。

また、赤黒木で厄介な割り当てのトリックをプレイしない限り、Bツリーの方がキャッシュ動作が優れていると推測するのが妥当と思われますローカリティをさらに増やします)。これは、スピードレースで役立つ可能性があります。

Bツリー(特にサイズパラメーター32および64)は、小さなサイズの赤黒ツリーと非常に競争力があり、適度に大きなn値でも優れたパフォーマンスを発揮するという実験的証拠を示すことができます。 http://idlebox.net/2007/stx-btree/stx-btree-0.8.3/doxygen-html/speedtest.html を参照してください

Bツリーは高速です。どうして?これは、メモリの局所性、キャッシュ動作の改善、およびポインター追跡の低下(同じものではないにしても、ある程度重複しているため)によると推測されます。

114
Jonas Kölker

実際、Wikipediaには、すべてのRBツリーをBツリーとして簡単に表現できることを示す素晴らしい記事があります。次のツリーをサンプルとして使用します。

RB-Tree

これをBツリーに変換するだけです(これをより明確にするために、ノードはまだR/Bに色付けされています。これは通常Bツリーにはありません)。

Bツリーと同じツリー

(奇妙な理由でここに画像を追加することはできません)

他のRBツリーについても同様です。この記事から引用したものです。

http://en.wikipedia.org/wiki/Red-black_tree

この記事から引用するには:

赤黒ツリーは構造的に4次のBツリーと同等であり、クラスターあたりの値の最小充填率は33%で、最大容量は3値です。

どちらか一方が他方よりもはるかに優れているというデータは見つかりませんでした。もしそうだとしたら、どちらか一方はすでに死んでいたと思います。メモリに保存する必要のあるデータの量と、ツリーからノードを追加/削除するのがどれほど複雑かという点で異なります。

更新:

私の個人的なテストでは、Bツリーはデータの局所性が優れているため、データを検索するときにBツリーの方が優れていることが示唆されています。 Bツリーの次数が高いほど(順序はノートが持つことができる子の数です)、検索が速くなります。一方、新しいエントリの追加と削除のパフォーマンスは、順序が高いほど低下します。これは、ノード内に値を追加すると線形の複雑さが生じるためです。各ノードはソートされた配列であるため、要素を中央に追加する場合、その配列内で多くの要素を移動する必要があります。新しい要素の左側にあるすべての要素を左側に1つ、右側にすべての要素を移動する必要があります新しい要素を1つ右に移動する必要があります。挿入中に値が1つのノードを上に移動する場合(Bツリーで頻繁に発生します)、すべての要素を左から1つ右に移動するか、すべての要素を左に1つ右の位置。これらの操作(通常Cでmemmoveによって実行される)は、実際にはO(n)です。したがって、Bツリーの次数が高いほど、ルックアップは高速になりますが、変更は遅くなります。一方、低すぎる順序(3など)を選択した場合、Bツリーは実際には他のツリー構造に比べて利点または欠点がほとんどありません(そのような場合、他のものを使用することもできます)。したがって、私は常に高次のBツリーを作成します(少なくとも4、8以上は問題ありません)。

多くの場合Bツリーに基づくファイルシステムは、はるかに高い次数(次数200以上)を使用します。これは、通常、ノート(許可される要素の最大数を含む場合)がハードドライブ上のセクターまたはファイルシステムのクラスターのサイズ。これにより、最適なパフォーマンス(HDは一度に1つのセクタのみを書き込むことができるため、1バイトだけを変更してもセクタ全体が書き換えられるため)と最適なスペース使用率(ドライブ上の各データエントリが少なくともデータが実際にどれほど大きいかに関係なく、1つのクラスターまたはクラスターサイズの倍数です)。ハードウェアがデータをセクターと見なし、ファイルシステムがセクターをクラスターにグループ化するという事実により、Bツリーは、他のツリー構造よりもはるかに優れたパフォーマンスとスペース使用率をファイルシステムにもたらします。それが、ファイルシステムで非常に人気がある理由です。

アプリが絶えずツリーを更新し、そこから値を追加または削除する場合、RB-TreeまたはAVL-Treeは、高次のB-Treeと比較して平均でより良いパフォーマンスを示す場合があります。ルックアップではやや悪化し、より多くのメモリが必要になる場合がありますが、通常、変更は高速です。実際には、RBツリーはAVLツリーよりも修正がさらに高速です。そのため、AVLツリーは通常、深さが浅いため、ルックアップでは少し高速です。

そのため、いつものように、アプリの動作に大きく依存します。私の推奨事項は次のとおりです。

  1. 多くの検索、わずかな変更:Bツリー(高次)
  2. 多くの検索、多くの変更:AVL-Tree
  3. 少しの検索、多くの変更:RB-Tree

これらすべてのツリーの代替は AA-Trees です。このように PDFペーパーが示唆する 、AAツリー(実際にはRBツリーのサブグループ)は通常のRBツリーとほぼ同等のパフォーマンスですが、RBよりも実装がはるかに簡単です-ツリー、AVLツリー、またはBツリー。ここに 完全な実装 、見てどれだけ小さいか(メイン関数は実装の一部ではなく、実装行は実際にはコメントです)。

PDF論文が示すように、 Treap は古典的なツリー実装の興味深い代替手段でもあります。Treapはバイナリツリーでもありませんが、不均衡なバイナリツリーで発生する可能性のある最悪のシナリオを回避するために(ルックアップがO(log n)ではなくO(n)になる))、Treatはランダム性を追加しますツリー:ランダム性は、ツリーのバランスが取れていることを保証することはできませんが、ツリーのバランスが極端に取れていない可能性も非常に高くなります。

92
Mecki

メモリ内でのみ動作するBツリーの実装を妨げるものは何もありません。実際、キー比較が安価な場合、メモリ内のBツリーはfasterになります。これは、1つのノードに複数のキーをパッキングすると、検索中にlessキャッシュミスが発生するためです。パフォーマンスの比較については、 this リンクを参照してください。引用:「スピードテストの結果は興味深いものであり、B +ツリーが16,000を超えるアイテムを含むツリーで非常に高速であることを示しています。」 (B + TreeはB-Treeの単なるバリエーションです)。

27
zvrba

質問は古いですが、まだ関連があると思います。 JonasKölkerとMeckiは非常に良い答えを出しましたが、答えが全体をカバーしているとは思いません。私は議論全体がポイントを欠いているとさえ主張します:-)。

エントリが比較的小さい場合(整数、小さな文字列/単語、フロートなど)、Bツリーについて言われたことは真実です。エントリが大きい(100Bを超える)場合、差は小さく/取るに足らないものになります。

Bツリーに関する主なポイントを要約します。

  • メモリの局所性により、バイナリ検索ツリー(BST)よりも高速です(キャッシュとTLBミスが少なくなります)。

  • エントリが比較的小さい場合、またはエントリのサイズが可変の場合、Bツリーは通常、スペース効率が高くなります。空き領域の管理が容易になり(メモリの大きなチャンクを割り当てる)、エントリごとの余分なメタデータのオーバーヘッドが低くなります。 Bツリーは、ノードが常にいっぱいではないため、スペースを浪費しますが、バイナリ検索ツリーよりもコンパクトになります。

  • Oの大きなパフォーマンス(O(logN))は両方で同じです。さらに、各Bツリーノード内でバイナリ検索を行うと、同じ数の比較になります。 BSTのように(これを検証するのは素晴らしい数学の演習です)Bツリーノードサイズが適切(1-4xキャッシュラインサイズ)である場合、ハードウェアプリフェッチにより各ノード内の線形検索はさらに高速になります。また、SIMD命令を使用して、基本データ型(整数など)を比較します。

  • Bツリーは圧縮に適しています。ノードごとに圧縮するデータが多くなります。場合によっては、これは大きなメリットになります。インデックスを作成するために使用されるリレーショナルデータベーステーブルの自動インクリメントキーを考えてください。 Bツリーのリードノードには、非常によく圧縮される連続した整数が含まれています。

  • Bツリーは、セカンダリストレージ(ブロックIOを実行する必要がある場所)に保存すると、明らかにはるかに高速です。

紙では、Bツリーには多くの利点があり、ほとんど欠点はありません。最高のパフォーマンスを得るには、Bツリーを使用する必要がありますか?

ツリーがメモリに収まる場合、答えは通常NOです。パフォーマンスが重要な場合、スレッドセーフなツリーのようなデータ構造が必要です(簡単に言えば、複数のスレッドが単一のスレッドよりも多くの作業を行うことができます)。 Bツリーを作成して同時アクセスをサポートすることは、BSTを作成するよりも問題が多くなります。ツリーが同時アクセスをサポートするようにする最も簡単な方法は、ノードをトラバース/変更しているときにノードをロックすることです。 Bツリーでは、ノードごとにより多くのエントリをロックするため、より多くのシリアル化ポイントと競合ロックが発生します。

すべてのツリーバージョン(AVL、Red/Black、B-Tree、その他)には、並行処理のサポート方法が異なる無数のバリアントがあります。大学のコースで学んだり、入門書から読んだりするバニラアルゴリズムは、実際にはほとんど使用されません。そのため、各ツリーの背後にある正確なアルゴリズムに関する公式の合意がないため、どのツリーが最適に機能するかを言うのは困難です。前述のツリーは、正確なデータ構造ではなく、特定のツリーのような不変式に従うデータ構造クラスのように考えることをお勧めします。

たとえば、Bツリーを見てください。バニラBツリーは実際に使用されることはほとんどありません。うまく拡張することはできません。使用される最も一般的なBツリーバリアントは、B +ツリー(ファイルシステム、データベースで広く使用されています)です。 B + -TreeとB-Treeの主な違い:1)エントリをツリーの内部ノードに格納しない(したがって、内部ノードに格納されたエントリを変更するときにツリーの高い書き込みロックを必要としない) ; 2)同じレベルのノード間にリンクがあります(したがって、範囲検索を行うときにノードの親をロックする必要はありません)。

これがお役に立てば幸いです。

10
Radu

GoogleのGuysは最近、Bツリーに基づいたSTLコンテナの実装をリリースしました。彼らは、バージョンが高速であり、赤黒木を介して実装された標準のSTLコンテナと比較してメモリの消費が少ないと主張しています。詳細 ここ

8

一部のアプリケーションでは、BツリーはBSTよりも大幅に高速です。ここにある木:

http://freshmeat.net/projects/bps

非常に高速です。また、ノードごとに2つまたは3つのポインターのBSTインフラストラクチャと、バランシング情報を保持するためのいくつかの追加フィールドを必要としないため、通常のBST実装よりも少ないメモリを使用します。

2

それらはすべて同じ漸近的な動作を持っているため、パフォーマンスは使用しているツリーのタイプよりも実装に大きく依存します。ツリー構造の組み合わせは、実際には最速のアプローチかもしれません。Bツリーの各ノードがキャッシュラインに正確に適合し、各ノード内の検索に何らかのバイナリツリーが使用されます。ノードのメモリを自分で管理することで、キャッシュの局所性をさらに高めることもできますが、非常に高価です。

個人的には、使用している言語の標準ライブラリにあるものを使用します。これは、非常に小さなパフォーマンスゲイン(もしあれば)を得るための多くの作業だからです。

理論的には... RBツリーは、実際には2-3ツリーの動作をシミュレートするため、Bツリーと非常によく似ています。 AAツリーは同様の構造で、代わりに2〜3本のツリーをシミュレートします。

0
Jørgen Fogh

さらに...赤黒木の高さはO(log [2] N)であるのに対し、B-treeの高さはO(log [q] N)で、ceiling [N] <= q <= Nです。したがって、Bツリーの各キー配列(上記のように固定されている)での比較を検討する場合、Bツリーの時間計算量<=赤黒ツリーの時間計算量です。 (ブロックサイズとサイズが等しい単一レコードの等しい場合)

0
mohit

通常、ストレージはディスクページであるため、Bノードはストレージでツリーノードをまとめる必要があるときに使用されます。そのため、再バランスは非常に高価になる可能性があります。この制約がない場合、RBツリーが使用されます。したがって、リレーショナルデータベースインデックスを(たとえば)実装する場合、Bツリーはおそらく高速になりますが、RBツリーは(たとえば)メモリ内検索ではおそらく高速になります。

0
anon