web-dev-qa-db-fra.com

B-tree plus rapide que AVL ou RedBlack-Tree?

Je sais que les performances ne sont jamais noires ou blanches. Souvent, une implémentation est plus rapide dans le cas X et plus lente dans le cas Y, etc. Ils sont considérablement plus complexes à implémenter que les arbres AVL (et peut-être même les arbres Red Black?), Mais sont-ils plus rapides (leur complexité est-elle payante)?

Edit: Je voudrais aussi ajouter que s’ils sont plus rapides, l’arbre équivalent AVL/RedBlack (en termes de nœuds/contenu) - pourquoi sont-ils plus rapides?

63
thr

Le message de Sean (celui qui est actuellement accepté) contient plusieurs revendications incorrectes. Désolé Sean, je ne veux pas être impoli; J'espère pouvoir vous convaincre que ma déclaration est fondée sur des faits.

Ils sont totalement différents dans leurs cas d'utilisation, il est donc impossible de faire une comparaison.

Ils sont tous deux utilisés pour maintenir un ensemble d’articles totalement ordonnés avec recherche, insertion et suppression rapides. Ils ont la même interface et la même intention.

Les arbres RB sont généralement des structures en mémoire utilisées pour fournir un accès rapide (idéalement O(logN)) aux données. [...]

toujours O (log n)

Les arbres B sont généralement des structures basées sur disque et sont donc intrinsèquement plus lents que les données en mémoire.

Absurdité. Lorsque vous stockez des arbres de recherche sur un disque, vous utilisez généralement des arbres B. C'est beaucoup vrai. Lorsque vous stockez des données sur un disque, l'accès est plus lent que les données en mémoire. Mais un arbre rouge-noir stocké sur le disque est aussi plus lent qu'un arbre rouge-noir stocké en mémoire.

Vous comparez des pommes et des oranges ici. Ce qui est vraiment intéressant est une comparaison des arbres B en mémoire et des arbres rouge-noir en mémoire.

[En passant: les arbres B, par opposition aux arbres rouge-noir, sont théoriquement efficaces dans le modèle I/O. J'ai testé (et validé) expérimentalement le modèle I/O pour le tri; Je m'attendrais à ce que cela fonctionne aussi pour les arbres B.]

Les arbres B sont rarement des arbres binaires, le nombre d'enfants qu'un nœud peut avoir est généralement un nombre élevé.

Pour être clair, la plage de taille des nœuds B-tree est un paramètre de l'arbre (en C++, vous pouvez utiliser une valeur entière en tant que paramètre de modèle).

La gestion de la structure B-Tree peut être assez compliquée lorsque les données changent.

Je me souviens qu'ils sont beaucoup plus simples à comprendre (et à mettre en œuvre) que les arbres rouge-noir.

B-tree essaye de minimiser le nombre d’accès au disque afin que la récupération des données soit raisonnablement déterministe.

C'est beaucoup vrai.

Il n'est pas rare de voir quelque chose comme 4 accès B-tree nécessaire pour rechercher un peu de données dans une base de données très.

Vous avez des données?

Dans la plupart des cas, je dirais que les arbres RB en mémoire sont plus rapides.

Vous avez des données?

Parce que la recherche est binaire, il est très facile de trouver quelque chose. B-tree peut avoir plusieurs enfants par nœud, vous devez donc analyser chaque nœud pour rechercher l'enfant approprié. C'est une opération O(N).

La taille de chaque nœud est un paramètre fixe. Même si vous effectuez un balayage linéaire, il s'agit de O (1). Si nous dépassons la taille de chaque nœud, notez que vous gardez le tableau trié de manière à ce que ce soit O (log n).

Sur un arbre RB, il s'agirait de O(logN) puisque vous effectuez une comparaison, puis une branche.

Vous comparez des pommes et des oranges. O (log n) est dû au fait que la hauteur de l’arbre est au maximum O (log n), tout comme pour un arbre B.

De plus, à moins que vous ne fassiez de mauvais tours d'allocation avec les arbres rouge-noir, il semble raisonnable de supposer que les arbres B ont un meilleur comportement de mise en cache (il accède à un tableau et non à des pointeurs éparpillés partout. localité encore plus), ce qui pourrait l’aider dans la course de vitesse.

Je peux citer des preuves expérimentales montrant que les arbres B (avec les paramètres de taille 32 et 64, en particulier) sont très compétitifs avec les arbres rouge-noir pour les petites tailles et le surpassent sans aucun doute pour des valeurs même modérément grandes de n. Voir http://idlebox.net/2007/stx-btree/stx-btree-0.8.3/doxygen-html/speedtest.html

Les arbres B sont plus rapides. Pourquoi? Je suppose que cela est dû à la localité de la mémoire, à un meilleur comportement de la mise en cache et à moins de poursuite de pointeur (qui sont, sinon les mêmes choses, qui se chevauchent dans une certaine mesure).

111
Jonas Kölker

En fait, Wikipedia a un excellent article qui montre que chaque arbre RB peut facilement être exprimé en arbre B. Prenez l’arbre suivant comme exemple:

RB-Tree

convertissez-le maintenant en B-Tree (pour que cela soit plus évident, les nœuds sont toujours colorés en R/B, ce que vous n’avez généralement pas dans un B-Tree):

même arbre que B-Tree

(impossible d'ajouter l'image ici pour une raison étrange)

Même chose pour tout autre arbre RB. C'est tiré de cet article:

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

Pour citer cet article:

L'arbre rouge-noir est alors structurellement équivalent à un arbre B d'ordre 4, avec un facteur de remplissage minimum de 33% des valeurs par cluster avec une capacité maximale de 3 valeurs.

Je n'ai trouvé aucune donnée indiquant que l'une des deux est significativement meilleure que l'autre. Je suppose que l'un des deux était déjà mort si c'était le cas. Ils sont différents en ce qui concerne la quantité de données qu'ils doivent stocker en mémoire et la difficulté d'ajouter ou de supprimer des nœuds de l'arborescence.

Mettre à jour:

Mes tests personnels suggèrent que les B-Trees sont meilleurs lors de la recherche de données, car ils ont une meilleure localisation des données, ce qui permet au cache de la CPU de comparer un peu plus rapidement. Plus l'ordre d'un arbre B est élevé (l'ordre correspond au nombre d'enfants qu'une note peut avoir), plus la recherche sera rapide. D'autre part, plus leur ordre est élevé, plus les performances d'ajout et de suppression de nouvelles entrées sont mauvaises. Cela est dû au fait que l'ajout d'une valeur dans un nœud a une complexité linéaire. Comme chaque nœud est un tableau trié, vous devez déplacer de nombreux éléments dans ce tableau lorsque vous ajoutez un élément au milieu: tous les éléments situés à gauche du nouvel élément doivent être déplacés d'une position à gauche ou tous les éléments à droite de le nouvel élément doit être déplacé d'une position vers la droite. Si une valeur déplace un noeud vers le haut pendant une insertion (ce qui se produit fréquemment dans un arbre B), elle laisse un trou qui doit également être rempli, soit en déplaçant tous les éléments de la gauche vers la droite, soit en déplaçant tous les éléments vers la droite une position à gauche. Ces opérations (en C habituellement effectuées par memmove) sont en fait O (n). Ainsi, plus l'ordre B-Tree est élevé, plus la recherche est rapide, mais plus la modification est lente. D'autre part, si vous choisissez un ordre trop bas (par exemple 3), un arbre B présente peu d'avantages ou d'inconvénients par rapport à d'autres structures arborescentes (dans ce cas, vous pouvez également utiliser autre chose). Ainsi, je créerais toujours des arbres B avec des ordres élevés (au moins 4, 8 et plus, c'est bien).

Les systèmes de fichiers, qui reposent souvent sur des arbres B, utilisent des ordres beaucoup plus élevés (ordre 200 et même beaucoup plus), car ils choisissent généralement un ordre suffisamment élevé pour qu’une note (contenant le nombre maximal d’éléments autorisés) soit égale. la taille d'un secteur sur le disque dur ou d'un cluster du système de fichiers. Cela donne des performances optimales (puisqu’un disque dur ne peut écrire qu’un secteur complet à la fois, même lorsqu’un seul octet est modifié, le secteur complet est néanmoins réécrit) et une utilisation optimale de l’espace un cluster ou un multiple de la taille du cluster, quelle que soit la taille réelle des données). En raison du fait que le matériel considère les données comme des secteurs et que le système de fichiers regroupe des secteurs en clusters, les arbres B-Trees peuvent générer des performances et une utilisation de l'espace bien meilleures pour les systèmes de fichiers que toute autre arborescence. c'est pourquoi ils sont si populaires pour les systèmes de fichiers.

Lorsque votre application met constamment à jour l'arborescence, y ajoute ou en supprime des valeurs, un arbre RB ou AVL peut afficher de meilleures performances en moyenne par rapport à un arbre B avec un ordre élevé. Un peu pire pour les recherches et ils pourraient également avoir besoin de plus de mémoire, mais les modifications sont donc rapides. En fait, les arbres RB sont encore plus rapides pour les modifications que les AVL-arbres, c'est pourquoi les arbres AVL sont un peu plus rapides pour les recherches car ils sont généralement moins profonds.

Donc, comme d'habitude, cela dépend beaucoup de ce que fait votre application. Mes recommandations sont:

  1. Beaucoup de recherches, petites modifications: B-Tree (avec ordre élevé)
  2. Beaucoup de recherches, beaucoup de modifications: AVL-Tree
  3. Petites recherches, beaucoup de modifications: RB-Tree

Une alternative à tous ces arbres est AA-Trees . Comme ceci le document PDF le suggère , les arbres AA (qui sont en fait un sous-groupe d'arbres RB) ont des performances presque égales à celles des arbres RB normaux, mais ils sont beaucoup plus faciles à implémenter que RB. -Trees, AVL-Trees ou B-Trees. Voici un implémentation complète , regardez comme il est minuscule (la fonction principale ne fait pas partie de l'implémentation et la moitié des les lignes d'implémentation sont en fait des commentaires).

Comme le montre le document PDF, un Treap est également une alternative intéressante à la mise en œuvre classique de l'arborescence. Un Treap est aussi un arbre binaire, mais qui n'essaie pas de forcer l'équilibrage. Pour éviter les pires scénarios que vous pourriez rencontrer dans des arbres binaires non équilibrés (provoquant des recherches devenant O(n) au lieu de O (log n)), un objet Treap ajoute un caractère aléatoire à l'arbre. Le caractère aléatoire ne peut garantir que l’arbre est bien équilibré, mais il est également très peu probable que l’arbre soit extrêmement déséquilibré.

92
Mecki

Rien n'empêche une implémentation B-Tree qui fonctionne uniquement en mémoire. En fait, si les comparaisons de clés sont peu coûteuses, le B-Tree en mémoire peut être plus rapide car son empilement de plusieurs clés dans un nœud entraînera moins de cache dans les recherches. Voir this link pour des comparaisons de performances. Une citation: "Les résultats des tests de vitesse sont intéressants et montrent que l'arbre B + est nettement plus rapide pour les arbres contenant plus de 16 000 éléments." (B + Tree est juste une variation sur B-Tree).

27
zvrba

La question est ancienne mais je pense qu'elle est toujours d'actualité. Jonas Kölker et Mecki ont donné de très bonnes réponses mais je ne pense pas que les réponses couvrent toute l'histoire. Je dirais même que toute la discussion passe à côté de l'essentiel :-). 

Ce qui a été dit à propos des B-Trees est vrai lorsque les entrées sont relativement petites (entiers, petites chaînes/mots, flottants, etc.). Lorsque les entrées sont grandes (plus de 100 milliards), les différences deviennent moins importantes/insignifiantes.

Permettez-moi de résumer les points principaux concernant les arbres B:

  • Ils sont plus rapides que tous les arbres de recherche binaire (BST) en raison de la localisation de la mémoire (ce qui entraîne moins de cache et d’absence de TLB).

  • Les arbres B sont généralement plus efficaces en termes d'espace si les entrées sont relativement petites ou si les entrées sont de taille variable. La gestion de l’espace libre est plus simple (vous allouez de gros morceaux de mémoire) et les métadonnées supplémentaires Le temps système par entrée est réduit. Les arbres B gaspillent de l’espace car les nœuds Ne sont pas toujours pleins, mais ils finissent toujours par être plus compacts Que les arbres de recherche binaires.

  • La grande performance O (O(logN)) est la même pour les deux. De plus, si vous effectuez une recherche binaire à l'intérieur de chaque nœud B-Tree, vous obtiendrez même le même nombre de comparaisons que dans un BST (c'est un exercice mathématique de Nice pour le vérifier). Si la taille du nœud B-Tree est raisonnable (taille de la ligne de cache 1-4x), la recherche linéaire à l'intérieur de chaque nœud est encore plus rapide en raison de la pré-extraction matérielle Vous pouvez également utiliser les instructions SIMD pour comparer les types de données de base (par exemple, des nombres entiers). 

  • Les arbres B sont mieux adaptés à la compression: il y a plus de données par nœud à compresser. Dans certains cas, cela peut représenter un avantage énorme… .. Il suffit de penser à une clé à incrémentation automatique dans une table de base de données relationnelle utilisée pour créer un index. Les noeuds principaux d'un arbre B contiennent des entiers consécutifs qui se compressent très, très bien.

  • Les arbres B sont clairement beaucoup, beaucoup plus rapides lorsqu'ils sont stockés sur un stockage secondaire (où vous devez bloquer l'IO).

Sur le papier, les arbres B ont de nombreux avantages et ne présentent pratiquement aucun inconvénient. Alors, faut-il simplement utiliser B-Trees pour de meilleures performances?

La réponse est généralement NON - si l’arbre tient dans la mémoire. Dans les cas où les performances sont cruciales, vous souhaitez une structure de données de type arborescence sécurisée pour les threads (simplement, plusieurs threads peuvent faire plus de travail qu'un seul). Il est plus problématique de faire en sorte que B-Tree prenne en charge les accès simultanés que de faire un BST. Le moyen le plus simple de faire en sorte qu'une arborescence prenne en charge les accès simultanés est de verrouiller les nœuds lorsque vous les parcourez/les modifiez. Dans un arbre B, vous verrouillez plus d'entrées par noeud, ce qui donne plus de points de sérialisation et plus de verrous contestés.

Toutes les versions de l'arborescence (AVL, Rouge/Noir, B-Tree, etc.) ont d'innombrables variantes qui diffèrent par la manière dont elles gèrent la simultanéité. Les algorithmes de Vanilla qui sont enseignés dans un cours universitaire ou lus dans des livres d'introduction ne sont presque jamais utilisés dans la pratique. Il est donc difficile de dire quel arbre fonctionne le mieux car il n’ya pas d’accord officiel sur les algorithmes exacts qui se trouvent derrière chaque arbre. Je suggérerais de penser aux arbres mentionnés plus comme des classes de structure de données qui obéissent à certains invariants ressemblant à des arbres plutôt qu'à des structures de données précises.

Prenons par exemple le B-Tree. Le Vanilla B-Tree n’est presque jamais utilisé dans la pratique - vous ne pouvez pas le faire à l’échelle! La variante B-Tree la plus couramment utilisée est l'arbre B + (largement utilisé dans les systèmes de fichiers, les bases de données). Les principales différences entre l’arbre B + et l’arbre B: 1) vous ne stockez pas les entrées dans les nœuds internes de l’arborescence (vous n’avez donc pas besoin de verrouiller l’écriture dans l’arbre lorsque vous modifiez une entrée stockée dans un nœud interne). ; 2) vous avez des liens entre les nœuds au même niveau (vous n'avez donc pas besoin de verrouiller le parent d'un nœud lorsque vous effectuez des recherches de plage).

J'espère que ça aide.

10
Radu

Les gars de Google ont récemment publié leur implémentation des conteneurs STL, basés sur des arbres B. Ils affirment que leur version est plus rapide et consomme moins de mémoire que les conteneurs STL standard, implémentés via des arbres rouge-noir . Plus de détails ici

8
Konstantin Chernov

Pour certaines applications, les arbres B sont nettement plus rapides que les BST . Les arbres que vous pouvez trouver ici:

http://freshmeat.net/projects/bps

sont assez rapides. Ils utilisent également moins de mémoire que les implémentations BST standard, car ils ne nécessitent pas l'infrastructure BST de 2 ou 3 pointeurs par nœud, plus quelques champs supplémentaires pour conserver les informations d'équilibrage.

2
Ciprian Niculescu

Ils ont tous le même comportement asymptotique, de sorte que les performances dépendent davantage de l'implémentation que du type d'arborescence que vous utilisez. Une combinaison de structures arborescentes peut en fait être l'approche la plus rapide, où chaque nœud d'un arbre B correspond exactement. dans une ligne de cache et une sorte d’arbre binaire est utilisé pour effectuer une recherche dans chaque nœud. La gestion de la mémoire des nœuds vous-même peut également vous permettre d’obtenir une localisation de cache encore plus grande, mais à un prix très élevé.

Personnellement, j’utilise tout ce qui est dans la bibliothèque standard pour le langage que j’utilise, car c’est beaucoup de travail pour un très petit gain de performance (le cas échéant).

Sur une note théorique ... Les arbres RB sont en réalité très similaires aux arbres B, car ils simulent le comportement de 2-3-4 arbres. Les arbres AA sont une structure similaire, qui simule plutôt 2 ou 3 arbres.

0
Jørgen Fogh

de plus ... la hauteur d'un arbre noir rouge est O (log [2] N) alors que celle de l'arbre B est O (log [q] N) où plafond [N] <= q <= N. Donc, si nous considérons des comparaisons dans chaque tableau de clés de B-tree (qui est fixé comme indiqué ci-dessus), alors la complexité temporelle de B-tree <= complexité temporelle de Red-black tree. (cas égal pour un enregistrement égal à la taille d'un bloc)

0
mohit

Ils sont utilisés dans des circonstances différentes - les arbres B sont utilisés lorsque les noeuds d'arbres doivent être conservés ensemble dans le stockage - généralement parce que le stockage est une page de disque et que le rééquilibrage peut donc être très coûteux. Les arbres RB sont utilisés lorsque vous n'avez pas cette contrainte. Ainsi, les arbres B seront probablement plus rapides si vous souhaitez implémenter (par exemple) un index de base de données relationnelle, tandis que les arbres RB seront probablement plus rapides pour (par exemple) une recherche en mémoire.

0
anon