Si j'implémente une opération de recherche en mémoire (RAM) avec des arbres b, serait-ce mieux en termes de mise en cache ou d'autres effets par rapport aux arbres binaires?
Ce que je sais c'est
binary search tress---O(log n)
btrees ---------------O(c log n)
il y a eu beaucoup de discussions à ce sujet sur divers blogs.
La complexité algorithmique est la même, puisque O (logb n) = O (c log n) = O (log n) mais les facteurs constants, qui sont cachés dans la notation big-O, peuvent varier sensiblement, selon l'implémentation et le matériel.
Les arbres B ont été conçus pour les disques durs Platter, qui ont un temps d'accès important (déplacement de la tête en position), après quoi un secteur physique entier est lu. Faire les nœuds de l'arbre B aussi grands que le secteur minimise le nombre de temps d'accès et maximise les données utiles de chaque opération de lecture.
Mais si vous manquez de mémoire, votre temps d'accès est négligeable, donc une meilleure comparaison consiste à compter le nombre de mots simples accédés par votre algorithme.
Par exemple, planifions une structure de données pour stocker 220 clés de 1 mot chacune, pour un total de 4 Mo de données brutes sur une machine 32 bits.
Un arbre de recherche binaire aura 220 nœuds, chacun contenant une clé et deux pointeurs (3 mots). La profondeur sera log2(220) = 20. La recherche moyenne devra lire la clé et l'un des pointeurs de chaque nœud sur son chemin, de la racine jusqu'en bas = 40 mots .
Un arbre B conçu pour les disques durs aura 4 nœuds. Chaque nœud pourrait être stocké en interne sous la forme d'un tableau trié de couples de clés et de pointeurs, entre 256 et 512 d'entre eux. À quoi ressemblera la recherche moyenne? Compte tenu d'un remplissage moyen de 3/4, chaque nœud contiendra 384 entrées, et sa recherche binaire interne devra consulter le journal moyen2(384) = 5,95 clés. La profondeur moyenne sera log384(220) = 2,33, donc notre recherche devra lire en moyenne 2,33 fois 5,95 touches, soit environ 14 mots .
Dans le cas d'un arbre B à faible fanout (facteur de branchement), avec chaque nœud contenant entre 16 et 32 clés, le remplissage moyen sera de 24 clés, le journal de la profondeur moyenne24(220) = 4,36, la recherche binaire dans chaque nœud fera le journal2(24) = 4,58 comparaisons, et la recherche moyenne globale devra lire environ 20 mots .
Gardez à l'esprit que les deux dernières structures de données obtiennent un meilleur résultat que les arbres binaires car elles optimisent les opérations de lecture sur les modifications. Pour insérer une clé dans l'un de ces arbres B, vous devrez réécrire en moyenne un nœud entier de 384 mots ou 24 mots, si ce n'est plus d'un, alors que dans le cas de l'arbre binaire, une opération d'écriture n'aurait besoin que de retouchez jusqu'à 40 mots.
(Auparavant, je me suis trompé. Merci à @virco et @Groo d'avoir signalé mon erreur dans les commentaires.)
Dans tous les cas, il semble que les arbres B en mémoire uniquement avec un faible fanout semblent fonctionner mieux que les arbres binaires dans la pratique .
32 clés par nœud en particulier semblent être un point idéal pour les architectures actuelles, à la fois 32 et 64 bits. De nombreux langages et bibliothèques plus récents utilisent des arbres B à 32 touches en tant que structure de données intégrée, à côté ou en remplacement des tables de hachage et des tableaux. Cette utilisation a été menée par Clojure et d'autres langages fonctionnels, mais a ensuite été reprise par des langages plus courants tels que Javascript, avec un accent récent sur les structures de données immuables (par exemple Immutable.js )
Ce résultat peut être expliqué non seulement en comptant le nombre de mots lus dans la mémoire, mais le cache manque également, qui sont des opérations de lecture qui provoquent le blocage du CPU et attendent la RAM. Si l'architecture de mise en cache peut récupérer des morceaux de RAM qui contiennent un nœud B-tree entier à la fois, nous obtenons la même optimisation qui a été utilisée avec succès pour le stockage de masse sur disque.
Dans le cas de structures de données optimisées pour le disque dur, nous utiliserions des arborescences B avec des nœuds aussi grands que le secteur du disque physique, pour minimiser les temps d'accès au disque. Dans ce cas, nous utilisons des arbres B avec des nœuds aussi grands que l'opération de lecture qui est effectuée par le cache de niveau 3 par rapport à la RAM, pour minimiser les ratés du cache.
Les arborescences B diffèrent des arborescences binaires en ce que les clés et les pointeurs sont regroupés en mémoire, vous obtenez donc un comportement de cache un peu meilleur à la fois sur le disque et en mémoire. Il n'y a cependant pas de différence d'exécution asymptotique (big-O).