web-dev-qa-db-fra.com

Amélioration des performances d'un très grand dictionnaire dans Python

Je constate que si j'initialise un dictionnaire vide au début, puis en ajoutant des éléments au dictionnaire dans une boucle for (environ 110 000 clés, la valeur de chaque clé est une liste, augmentant également dans la boucle), la vitesse diminue comme pour la boucle va.

Je soupçonne que le problème est que le dictionnaire ne connaît pas le nombre de clés au moment de l'initialisation et qu'il ne fait pas quelque chose de très intelligent, alors peut-être que la collision de stockage devient assez souvent et ralentit.

Si je connais le nombre de clés et quelles sont exactement ces clés, existe-t-il un moyen dans python de faire fonctionner un dict (ou une table de hachage) plus efficacement? Je me souviens vaguement que si vous connaissez le clés, vous pouvez concevoir la fonction de hachage intelligemment (hachage parfait?) et allouer l'espace au préalable.

52
szli

Si je connais le nombre de clés et quelles sont exactement ces clés, existe-t-il un moyen dans python de faire fonctionner un dict (ou une table de hachage) plus efficacement? Je me souviens vaguement que si vous connaissez le clés, vous pouvez concevoir la fonction de hachage intelligemment (hachage parfait?) et allouer l'espace au préalable.

Python n'expose pas une option de pré-dimensionnement pour accélérer la "phase de croissance" d'un dictionnaire, et ne fournit aucun contrôle direct sur le "placement" dans le dictionnaire.

Cela dit, si les clés sont toujours connues à l'avance, vous pouvez les stocker dans un set et créer vos dictionnaires à partir du définir à l'aide de dict.fromkeys () . Cette méthode de classe est optimisée pour pré-dimensionner le dictionnaire en fonction de la taille définie et elle peut remplir le dictionnaire sans aucun nouvel appel à __hash __ ():

>>> keys = {'red', 'green', 'blue', 'yellow', 'orange', 'pink', 'black'}
>>> d = dict.fromkeys(keys)  # dict is pre-sized to 32 empty slots

Si la réduction des collisions est votre objectif, vous pouvez exécuter des expériences sur l'ordre d'insertion dans le dictionnaire pour minimiser les empilements. (Jetez un oeil à variation de Brent sur l'algorithme D dans le TAOCP de Knuth pour avoir une idée de la façon dont cela est fait).

En instrumentant un pur Python pour les dictionnaires (tel que celui-ci ), il est possible de compter le nombre moyen pondéré de sondes pour un autre ordre d'insertion. Pour par exemple, l'insertion de dict.fromkeys([11100, 22200, 44400, 33300]) fait en moyenne 1,75 sonde par recherche, ce qui dépasse la moyenne de 2,25 sondes par recherche pour dict.fromkeys([33300, 22200, 11100, 44400]).

Une autre "astuce" consiste à augmenter la sensibilisation dans un dictionnaire entièrement rempli en le trompant en augmentant sa taille sans ajouter de nouvelle clé s:

 d = dict.fromkeys(['red', 'green', 'blue', 'yellow', 'orange'])
 d.update(dict(d))     # This makes room for additional keys
                       # and makes the set collision-free.

Enfin, vous pouvez introduire votre propre __hash __ () personnalisé pour vos clés dans le but d'éliminer toutes les collisions (peut-être en utilisant un générateur de hachage parfait tel que gperf ).

109
Raymond Hettinger