MISE À JOUR DU 2009-12-04 : Pour les résultats du profilage d'un certain nombre de suggestions postées ici, voir ci-dessous !
La question
Considérez la méthode suivante, très inoffensive et très simple, qui utilise un fichier switch
pour retourner une valeur d'énumération définie :
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
if (ActivCode == null) return MarketDataExchange.NONE;
switch (ActivCode) {
case "": return MarketDataExchange.NBBO;
case "A": return MarketDataExchange.AMEX;
case "B": return MarketDataExchange.BSE;
case "BT": return MarketDataExchange.BATS;
case "C": return MarketDataExchange.NSE;
case "MW": return MarketDataExchange.CHX;
case "N": return MarketDataExchange.NYSE;
case "PA": return MarketDataExchange.ARCA;
case "Q": return MarketDataExchange.NASDAQ;
case "QD": return MarketDataExchange.NASDAQ_ADF;
case "W": return MarketDataExchange.CBOE;
case "X": return MarketDataExchange.PHLX;
case "Y": return MarketDataExchange.DIRECTEDGE;
}
return MarketDataExchange.NONE;
}
Aujourd'hui, mon collègue et moi avons échangé quelques idées sur la manière de rendre cette méthode plus rapide. Nous avons proposé quelques modifications intéressantes qui ont permis d'améliorer les performances de manière significative (proportionnellement, bien sûr). J'aimerais savoir si d'autres personnes ont des idées d'optimisation qui ne nous sont pas venues à l'esprit.
Tout de suite, laissez-moi vous donner un petit avertissement : c'est pour amusant y pas pour alimenter tout le débat "optimiser ou ne pas optimiser". Cela dit, si vous comptez parmi ceux qui croient dogmatiquement que "l'optimisation prématurée est la racine de tous les maux", sachez que je travaille pour une société de trading à haute fréquence, où tout doit fonctionner le plus rapidement possible, qu'il y ait ou non un goulot d'étranglement. Donc, même si je poste ceci sur SO pour amusant il ne s'agit pas non plus d'une énorme perte de temps.
Une dernière remarque rapide : deux types de réponses m'intéressent : celles qui supposent que chaque entrée est un ActivCode valide (une des chaînes de caractères dans le champ "ActivCode") et celles qui supposent que chaque entrée est un code d'activation. switch
ci-dessus), et ceux qui ne le font pas. Je suis presque Je suis certain que le fait de faire la première hypothèse permet d'améliorer encore la vitesse ; en tout cas, c'est ce qui s'est passé pour nous. Mais je sais que des améliorations sont possibles dans les deux cas.
Les résultats
Eh bien, il s'avère que la solution la plus rapide jusqu'à présent (que j'ai testée) est venue de João Angelo, dont la suggestion était en fait très simple, mais extrêmement intelligente. La solution que mon collègue et moi avons élaborée (après avoir essayé plusieurs approches, dont beaucoup ont été imaginées ici aussi) est arrivée en deuxième position ; j'allais la poster, mais il s'avère que Mark Ransom a eu exactement la même idée, alors allez voir sa réponse !
Depuis que j'ai effectué ces tests, d'autres utilisateurs ont posté des messages de même nature. plus récent des idées... Je les testerai en temps voulu, lorsque j'aurai quelques minutes de plus à ma disposition.
J'ai effectué ces tests sur deux machines différentes : mon ordinateur personnel à la maison (un Athlon double cœur avec 4 Go de RAM sous Windows 7 64 bits) et ma machine de développement au travail (un Athlon double cœur avec 2 Go de RAM sous Windows XP SP3). Évidemment, les temps étaient différents ; cependant, les relatif Les temps, c'est-à-dire la façon dont chaque méthode se compare à toutes les autres, étaient les mêmes. C'est-à-dire que le plus rapide était le plus rapide sur les deux machines, etc.
Maintenant, les résultats. (Les heures que j'affiche ci-dessous proviennent de mon ordinateur personnel).
Mais d'abord, pour référence, la déclaration originale de l'interrupteur :
1000000 passages : 98,88 ms
Moyenne : 0,09888 microseconde
Les optimisations les plus rapides jusqu'à présent :
-
L'idée de João Angelo d'attribuer des valeurs aux enums en se basant sur les codes de hachage des chaînes ActivCode, puis de les caser directement
ActivCode.GetHashCode()
aMarketDataExchange
:
1000000 passages : 23,64 ms
Moyenne : 0,02364 microseconde
Augmentation de la vitesse : 329.90% -
L'idée de mon collègue et moi de faire un casting
ActivCode[0]
à unint
et de récupérer lesMarketDataExchange
à partir d'un tableau initialisé au démarrage (cette même idée a été suggérée par Mark Ransom) :
1000000 passages : 28,76 ms
Moyenne : 0,02876 microseconde
Augmentation de la vitesse : 253.13% -
l'idée de l'auteur de commuter sur la sortie de
ActivCode.GetHashCode()
au lieu deActivCode
:
1000000 passages : 34,69 ms
Moyenne : 0,03469 microseconde
Augmentation de la vitesse : 185.04% -
L'idée, suggérée par plusieurs utilisateurs dont Auraseer, tster, et kyoryu, d'allumer
ActivCode[0]
au lieu deActivCode
:
1000000 passages : 36,57 ms
Moyenne : 0,03657 microseconde
Augmentation de la vitesse : 174.66% -
L'idée de Loadmaster d'utiliser le hachage rapide,
ActivCode[0] + ActivCode[1]*0x100
:
1000000 passages : 39,53 ms
Moyenne : 0,03953 microseconde
Augmentation de la vitesse : 153.53% -
L'utilisation d'une table de hachage (
Dictionary<string, MarketDataExchange>
), comme cela a été suggéré par beaucoup :
1000000 passages : 88,32 ms
Moyenne : 0,08832 microseconde
Augmentation de la vitesse : 12.36% -
En utilisant une recherche binaire :
1000000 passages : 1031 ms
Moyenne : 1,031 microsecondes
Augmentation de la vitesse : aucune (les performances se sont dégradées)
Permettez-moi de dire que c'était vraiment cool de voir combien d'idées différentes les gens avaient sur ce problème simple. C'était très intéressant pour moi, et je suis très reconnaissant à tous ceux qui ont contribué et fait une suggestion jusqu'à présent.