web-dev-qa-db-fra.com

Optimisation des recherches: recherches de clés par dictionnaire et recherches d'index de tableaux

J'écris un évaluateur de main de poker 7 cartes comme l'un de mes projets pour animaux de compagnie. Tout en essayant d'optimiser sa vitesse (j'aime le défi), j'ai été choqué de constater que les performances des recherches de clés dans le Dictionnaire étaient assez lentes par rapport aux recherches dans l'index des tableaux.

Par exemple, j’ai exécuté cet exemple de code qui énumère les 52 options suivantes: choisissez 7 = 133 784 560 possibilités de 7 mains de cartes:

var intDict = new Dictionary<int, int>();
var intList = new List<int>();
for (int i = 0; i < 100000; i ++)
{
    intDict.Add(i, i);  
    intList.Add(i);
}

int result;

var sw = new Stopwatch();
sw.Start();
for (int card1 = 0; card1 < 46; card1++)
  for (int card2 = card1 + 1; card2 < 47; card2++)
    for (int card3 = card2 + 1; card3 < 48; card3++)
      for (int card4 = card3 + 1; card4 < 49; card4++)
        for (int card5 = card4 + 1; card5 < 50; card5++)
          for (int card6 = card5 + 1; card6 < 51; card6++)
            for (int card7 = card6 + 1; card7 < 52; card7++)
              result = intDict[32131]; // perform C(52,7) dictionary key lookups
sw.Stop();
Console.WriteLine("time for dictionary lookups: {0} ms", sw.ElapsedMilliseconds);

sw.Reset();

sw.Start();
for (int card1 = 0; card1 < 46; card1++)
  for (int card2 = card1 + 1; card2 < 47; card2++)
    for (int card3 = card2 + 1; card3 < 48; card3++)
      for (int card4 = card3 + 1; card4 < 49; card4++)
        for (int card5 = card4 + 1; card5 < 50; card5++)
          for (int card6 = card5 + 1; card6 < 51; card6++)
            for (int card7 = card6 + 1; card7 < 52; card7++)
              result = intList[32131]; // perform C(52,7) array index lookups
sw.Stop();
Console.WriteLine("time for array index lookups: {0} ms", sw.ElapsedMilliseconds);

qui produit:

time for dictionary lookups: 2532 ms
time for array index lookups: 313 ms

Ce type de comportement est-il attendu (diminution de la performance d'un facteur 8)? IIRC, un dictionnaire a, en moyenne, O(1) recherches, alors qu'un tableau contient le pire cas O(1), donc je m'attends à ce que les recherches de tableau soient plus rapides , mais pas par là!

Je suis en train de stocker le classement des mains de poker dans un dictionnaire. Je suppose que si cela est aussi rapide que les recherches dans le dictionnaire, je dois repenser mon approche et utiliser des tableaux, bien que l'indexation du classement devienne un peu délicate et que je devrai probablement poser une autre question à ce sujet.

28
snazzer

N'oubliez pas que les notations Big-O indiquent uniquement comment la complexité augmente en fonction de la taille (etc.) - cela ne donne aucune indication sur les facteurs constants impliqués. C'est pourquoi parfois, même un recherche linéaire pour les clés est plus rapide qu'une recherche dans un dictionnaire, lorsqu'il y a suffisamment de clés. Dans ce cas, vous ne faites même pas de recherche avec le tableau, mais une simple opération d'indexation.

Pour les recherches indexées droites, les tableaux sont fondamentalement idéaux - c'est juste un cas de 

pointer_into_array = base_pointer + offset * size

(Et ensuite un déréférencement de pointeur.)

Effectuer une recherche dans le dictionnaire est relativement compliqué - très rapide comparé à (par exemple) une recherche linéaire par clé lorsqu'il y a beaucoup de clés, mais beaucoup plus compliqué qu'une recherche par tableau simple. Il doit calculer le hachage de la clé, puis déterminer le compartiment dans lequel se trouver, éventuellement gérer les hachages dupliqués (ou les compartiments dupliqués), puis vérifier l’égalité.

Comme toujours, choisissez la bonne structure de données pour le travail - et si vous pouvez vraiment vous en sortir en indexant simplement dans un tableau (ou List<T>), alors oui, ce sera extrêmement rapide.

57
Jon Skeet

Ce type de comportement est-il attendu (diminution de la performance d'un facteur 8)?

Pourquoi pas? Chaque recherche de tableau est presque instantanée/négligeable, alors qu'une recherche par dictionnaire peut nécessiter au moins un appel de sous-programme supplémentaire.

Le point où ils sont tous deux O(1) signifie que même si vous avez 50 fois plus d'éléments dans chaque collection, la diminution des performances reste un facteur de ce qu'elle est (8).

8
ChrisW

Quelque chose pourrait prendre un millénaire et rester O (1).

Si vous parcourez ce code en une seule étape dans la fenêtre de désassemblage, vous comprendrez rapidement la différence.

5
Mike Dunlavey

Les structures de dictionnaire sont particulièrement utiles lorsque l'espace clé est très grand et ne peuvent pas être mappées dans un ordre séquencé stable. Si vous pouvez convertir vos clés en un entier simple dans une plage relativement petite, vous aurez du mal à trouver une structure de données plus performante qu'un tableau.

Sur une note d'implémentation; Dans .NET, les dictionnaires sont essentiellement des hashables. Vous pouvez améliorer quelque peu leurs performances en matière de recherche de clé en veillant à ce que vos clés soient dissociées dans un grand espace de valeurs uniques. Il semble que dans votre cas, vous utilisiez un entier simple comme clé (ce qui, à mon avis, est un hachage à sa propre valeur), de sorte que vous ferez peut-être mieux.

3
LBushkin

Une recherche sur un tableau est la chose la plus rapide que vous puissiez faire. Elle consiste essentiellement en une arithmétique de pointeur permettant de passer du début du tableau à l’élément recherché. D'autre part, la recherche dans le dictionnaire risque d'être un peu plus lente car elle doit effectuer un hachage et se préoccuper de trouver le bon compartiment. Bien que l'exécution attendue soit également O(1), les constantes algorithmiques étant plus grandes, le processus sera plus lent.

2
1800 INFORMATION

Bienvenue dans la notation Big-O. Vous devez toujours considérer qu'il y a un facteur constant impliqué.

Faire une recherche dictée est bien sûr beaucoup plus coûteux qu'une recherche de tableau.

Big-O vous dit seulement comment les algorithmes évoluent. Doublez le nombre de recherches et voyez comment les chiffres changent: les deux devraient prendre environ deux fois plus de temps.

2
ebo

Le coût de la récupération d'un élément d'un dictionnaire est de O(1) , mais c'est parce qu'un dictionnaire est implémenté sous forme de table de hachage. revenir. Les tables de hachage ne sont souvent pas aussi efficaces, mais elles conviennent aux grands ensembles de données ou aux ensembles de données comportant de nombreuses valeurs de hachage uniques.

La liste (en plus d'être un mot nul utilisé pour dercribuer un tableau plutôt qu'une liste chaînée!) Sera plus rapide car elle renverra la valeur en calculant directement l'élément que vous voulez renvoyer.

0
gbjbaanb