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
Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top