7
votes

Est-il plus rapide de copier une référence à un objet de dictionnaire ou d'y accéder directement à partir du dictionnaire?

question est simple

est ce code xxx

même rapide que ce code xxx

sens commun Moi qu'il devrait être plus rapide de rechercher la référence dans le dictionnaire une fois et rangez-la quelque part afin de ne pas avoir besoin de regarder plusieurs fois, mais je ne sais pas vraiment comment fonctionne le dictionnaire.

Alors est-ce plus rapide ou pas? Et pourquoi?


11 commentaires

Avoir vous effectivement essayé de tester s'il existe une différence de vitesse?


pourquoi ne pas exécuter un test de référence et voir


Non je n'ai pas, je n'ai aucune idée de savoir comment le code de référence


@Petr disant que vous n'avez aucune idée de comment faire quelque chose dans la programmation est comme néo dans la matrice disant qu'il ne sait pas kung-fu . Il suffit de faire une recherche Google de 30 secondes et de Voila.


Eh bien, vous écrivez d'abord quelque chose comme DateTime StarTime = DateTime.now . Et puis vous écrivez le code que vous souhaitez comparer, puis vous écrivez datetime dt = datetime.now.frames - starttime.frames


La vitesse n'est guère pertinente dans un cas comme celui-ci, mais je penserais que vous avez raison, le dernier est le plus rapide, même s'il pourrait dépendre du nombre de propriétés (ou de champs) que vous devez attribuer à. Est-ce vraiment quatre? Je pense que vous devriez choisir à quelle jamais vous préférez. La vitesse n'est pas importante ici.


@Samiam Il serait préférable d'utiliser la classe chronomètre que pour s'appuyer sur datetime.now . En outre, en .net le DateTime STRUT n'a pas de Cadres Propriété. Vous pouvez directement soustraire deux valeurs DateTime (le résultat étant un Timespan valeur).


Est-ce une certitude que ces deux approches génèrent même un code IL différent?


@Samiam Je suis un débutant pour analyser, mais je sais qu'il existe plusieurs optimisations derrière l'exécution du code et les résultats ne sont pas toujours clairs. Par exemple, dans ce cas même, le compilateur pouvait simplement modifier le code sur ce que j'ai dans le deuxième bloc, pour le rendre plus rapide, mais ce cas complexe, il le laisserait comme il l'est (donc dans un cas je ne verrais aucune différence de vitesse et Dans un autre cas, il pourrait y en avoir certains). C'est pourquoi je demande ici, les gens qui connaissent vraiment la réponse, plutôt que d'essayer de le calculer moi-même


@Samiam je pense que vous voulez ticks , pas cadres , non? Et je suis d'accord avec @jeppestignielsen - le chronomètre a été conçu pour cela, alors j'irais avec elle.


Non, le compilateur n'est pas autorisé à "optimiser" le premier code dans l'équivalent du deuxième code, car valeurs est un champ public et que le compilateur ne peut pas savoir si certains L'autre thread sera en cours d'exécution qui modifie l'entrée pour "foo" , il doit donc rechercher à chaque fois si vous demandez cela.


3 Réponses :


7
votes

Oui, vous êtes correct. Votre première approche fait que le dictionnaire recherche 4 fois, alors que le second le fait une fois. La seconde est définitivement meilleure.

Cependant, dans la vie réelle, une recherche de dictionnaire est ridiculement rapide, alors si vous n'avez pas eu un dictionnaire massif, la différence ne sera pas perceptible, peut-être même pas mesurable.


6 commentaires

Remarque La taille du dictionnaire n'est pas pertinente ici, car les recherches sont O (1). Tout ce qui compte, c'est combien de recherches que vous faites, donc si ce code est dans une boucle qui fonctionne un grand nombre de fois (même si le dictionnaire étant levé n'est pas grand), il pourrait alors compter.


Notable, probablement pas. Mesurable, certainement. Le code doit calculer la valeur hachage et faire une recherche. De plus, la taille du dictionnaire est hors de propos. Vous recherchez quelque chose dans un dictionnaire de 1 000 000 articles ne prendra plus que dans un dictionnaire de seulement 1 000 articles. Sauf si le dictionnaire est plein (facteur de charge élevé). Ensuite, cela agit plus comme une recherche séquentielle.


Pour le plaisir, j'ai créé des dictionnaires de 1 000 et 1 000 000 articles à plusieurs reprises, puis 5 000 000 recherches sur elles (clé GUID). Il a fallu environ 2,5 secondes pour effectuer les recherches contre 1 000 articles et 3,5 secondes pour les faire contre 1 000 000 articles. Résultats très cohérents. Donc, je dirais que la taille du dictionnaire compte, bien que pas de façon spectaculaire.


@Jimmischel Vous avez raison, il doit être mesurable d'une certaine manière. Mais éventuellement pas mesurable en millisecondes, même pour beaucoup de recherches réelles de la vie réelle, ce que j'avais à l'esprit. Mais il est si facile d'optimiser cela que je ne le sauterais pas simplement parce que cela ne sera probablement pas un facteur.


@Joeenos dans votre test de performance, avez-vous consulté le même article, ou c'était des éléments aléatoires?


@niry il y a trois ans et demi, je ne pense pas que j'ai encore ce code ... mais il devrait être assez facile de recréer si vous voulez l'essayer.



1
votes

J'étais curieux. Le test de l'unité suivant (éventuellement) montre que la deuxième méthode est d'environ 25% plus rapide. (121 ms vs 91 ms). Aller de 6 champs à 2 réduit l'écart, 40 ms vs 33 ms. Je dis éventuellement depuis que j'ai écrit cela assez rapidement et je ne suis pas convaincu qu'il est immunisé de mesurer un effet secondaire, mais cela montre le comportement attendu, alors pourquoi la questionnera. (hah).

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;

namespace TestProject1
{

public class DataObject
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    public string D { get; set; }
    public string E { get; set; }
    public string F { get; set; }
}

[TestClass]
public class UnitTest1
{
    public static Dictionary<string, DataObject> dict = new Dictionary<string, DataObject>();
    static string lookie;

    [ClassInitialize()]
    public static void MyClassInitialize(TestContext testContext) {

        Random rand = new Random(123545);
        for (int i = 0; i < 10000; i++)
        {
            string key = rand.NextDouble().ToString();
            DataObject dob = new DataObject();
            dict.Add(key, dob);
            if (i == 4567)
                lookie = key;
        }

    }


    [TestMethod]
    public void TestMethod()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int j = 0; j < 100000; j++)
        {
            dict[lookie].A = "val" + j;
            dict[lookie].B = "val" + j;
            dict[lookie].C = "val" + j;
            dict[lookie].D = "val" + j;
            dict[lookie].E = "val" + j;
            dict[lookie].F = "val" + j;
        }
        sw.Stop();

        System.Diagnostics.Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Reset();
        sw.Start();
        for (int j = 0; j < 100000; j++)
        {
            DataObject dob = dict[lookie];
            dob.A = "val" + j;
            dob.B = "val" + j;
            dob.C = "val" + j;
            dob.D = "val" + j;
            dob.E = "val" +j;
            dob.F = "val" +j;
        }
        sw.Stop();

        System.Diagnostics.Debug.WriteLine(sw.ElapsedMilliseconds);
    }
  }
}


1 commentaires

Avez-vous fait les timings sur une version de sortie sans débogueur attachée? 25% semble ... excessif.



6
votes

Joe est absolument juste, mais comme si cela ne suffisait pas, j'ai fait un test évident simple: xxx

sur ma machine ces sorties à: xxx

Avoir seulement 1 recherche réduit définitivement le temps total des grands nombres.


1 commentaires

Donc, environ 1,3 seconde, différence sur 10 millions d'itérations? C'est à peu près la définition de «bas du bruit», en ce qui concerne l'optimisation du programme.