Ce qui est plus efficace un cas de commutation ou un std :: carte
Question
Je pense à la tokenizer ici.
Chaque jeton appelle une fonction différente à l'intérieur de l'analyseur.
Ce qui est plus efficace:
- Une carte de std :: fonctions / boost :: fonctions
- Un cas de commutation
La solution
Carte STL qui est livré avec Visual Studio 2008 vous donnera O (log (n)) pour chaque appel de fonction car il cache une structure arborescente sous. Avec compilateur moderne (en fonction de la mise en œuvre), une instruction switch vous donnera O (1), le compilateur, il se traduit par une sorte de table de consultation. Donc, en général, le commutateur est plus rapide.
Cependant , tenez compte des faits suivants:
La différence entre la carte et le commutateur est que: Map peut être construit de façon dynamique alors que le commutateur ne peut pas. Carte peut contenir tout type arbitraire comme une clé alors que l'interrupteur est très limité à c ++ types primitifs (char, int, ENUM, etc ...).
Par ailleurs, vous pouvez utiliser une carte de hachage pour atteindre près de O (1) l'envoi (bien que, en fonction de la mise en œuvre de la table de hachage, il peut parfois être O (n) au pire des cas). Même si, le commutateur sera encore plus rapide.
Modifier
Je vous écris ce qui suit que pour le plaisir et pour la question de la discussion
Je peux suggérer une bonne optimisation pour vous, mais cela dépend de la nature de votre langue et si vous pouvez vous attendre comment votre langue sera utilisée.
Lorsque vous écrivez le code: Vous divisez vos jetons en deux groupes, un groupe sera de très haut fréquemment utilisé et l'autre de bas fréquemment utilisé. Vous triez également les jetons élevés fréquemment utilisés. Pour la haute tokens souvent vous écrivez un if-else série avec le plus fréquemment utilisé à venir en premier. pour le bas fréquemment utilisé, vous écrivez une instruction switch.
L'idée est d'utiliser la prédiction de branchement du processeur afin d'éviter même un autre niveau d'indirection (en supposant que la condition de vérifier dans l'instruction if est presque sans coût). dans la plupart des cas, la CPU choisira la branche correcte sans aucun niveau d'indirection. Ils seront quelques cas, cependant, que la branche va au mauvais endroit. En fonction de la nature de votre languege, Statisticly il peut donner une meilleure performance.
Modifier :. En raison des commentaires ci-dessous, a changé la phrase disant que les compilateurs allways traduire un passage à LUT
Autres conseils
Je vous suggère de lire commutateur () vs recherche table? de Joel avec le logiciel. En particulier, cette réponse est intéressante:
» exemple de personnes perdre du temps en essayant d'optimiser le moins chose importante. "
Oui et non. Dans une machine virtuelle, vous généralement appelez minuscules fonctions que chaque font très peu. Il est le pas à l'appel / retour que vous Hurts autant que le préambule et la routine de nettoyage pour chaque fonction étant souvent un pourcentage important du temps d'exécution. Cela a été des recherches à la mort, en particulier par les personnes qui ont mis en œuvre taraudée interprètes.
Dans les machines virtuelles, des tables de consultation stockant les adresses calculées à appeler sont généralement préférés aux commutateurs. (Filetage direct ou « étiquette en tant que valeurs ». Appelle directement l'adresse de l'étiquette stockée dans la table de consultation) C'est parce qu'il permet, sous certaines conditions, de réduire Le comportement de efficace Interprètes de la machine virtuelle sur Architectures modernes
Mais comme mentionné, je suis sûr que ces détails ne sont pas pertinentes pour votre code . Ce sont des petits détails, et vous ne devriez pas trop se concentrer là-dessus. interpréteur Python est à l'aide des commutateurs, parce qu'ils pensent qu'il rend le code plus lisible. Pourquoi ne pas choisir l'utilisation que vous êtes le plus à l'aise? Impact sur les performances sera plutôt faible, vous feriez mieux de mettre l'accent sur la lisibilité du code pour l'instant;)
Modifier : S'il importe, en utilisant une table de hachage toujours être plus lent que d'une table de consultation. Pour une table de consultation, vous utilisez des types de ENUM pour vos « clés », et la valeur est récupéré à l'aide d'un seul saut indirect. Ceci est une seule opération d'assemblage. O (1). Une table de hachage recherche nécessite d'abord de calculer un hachage, puis pour récupérer la valeur, ce qui est beaucoup plus cher.
L'utilisation d'un tableau où les adresses de fonction sont stockées et accessibles en utilisant les valeurs d'un ENUM est bon. Mais en utilisant une table de hachage pour faire la même chose ajoute une surcharge importante
Pour résumer, nous avons:
- coût (Hash_table) >> coût (direct_lookup_table)
- coût (direct_lookup_table) ~ = coût (switch) si votre compilateur se traduit par des commutateurs dans les tables de recherche.
- coût (switch) >> coût (direct_lookup_table) (O (N) vs O (1)) si votre compilateur ne se traduit pas les commutateurs et les conditionals, mais je ne peux pas penser à un compilateur faire.
- Mais filetage inline directe rend le code moins lisible.
Quelle est votre définition de « efficace »? Si vous voulez dire plus rapide, alors vous devriez probablement le profil du code de test pour une réponse définitive. Si vous êtes après flexible et facile à étendre le code bien, alors vous une faveur et d'utiliser l'approche de la carte. Tout le reste est tout simplement l'optimisation prématurée ...
Comme yossi1981 dit, un interrupteur pourrait être optimisé de beeing une recherche table rapide, mais il n'y a aucune garantie, chaque compilateur a d'autres algorithmes pour déterminer si pour mettre en œuvre le commutateur comme consécutifs si ce ou aussi rapide table de consultation, ou peut-être une combinaison des deux.
Pour obtenir un changement rapide de vos valeurs doit respecter la règle suivante: ils doivent être consécutifs, qui est par exemple 0,1,2,3,4. Vous pouvez laisser certaines valeurs, mais des choses comme 0,1,2,34,43 sont très peu susceptibles d'optimiser.
La question est vraiment: la performance est d'une telle importance dans votre application? Et ne serait pas une carte qui charge ses valeurs dynamiquement à partir d'un fichier plus lisible et maintenable au lieu d'une déclaration énorme qui couvre plusieurs pages de code?
Vous ne dites pas quel type vos jetons sont. Si elles ne sont pas des entiers, vous ne disposez pas d'un choix - commutateurs seulement travailler avec les types entiers.
La norme C ++ ne dit rien sur les performances de ses besoins, mais seulement que la fonctionnalité devrait être là.
Ce genre de questions sur ce qui est mieux ou plus rapide ou plus efficace sont de sens que si vous déclarez que la mise en œuvre vous parlez. Par exemple, la chaîne de manutention dans une certaine version d'une certaine mise en œuvre de JavaScript était atroce, mais vous ne pouvez pas extrapoler que d'être une caractéristique de la norme pertinente.
J'irais même jusqu'à dire qu'il n'a pas d'importance, peu importe la mise en œuvre puisque la fonctionnalité fournie par switch
et std::map
est différent (bien qu'il y ait chevauchement).
Ces sortes de micro-optimisations sont presque jamais nécessaire, à mon avis.