11
votes

Déclaration de commutation avec un grand nombre de cas

Que se passe-t-il si le commutateur a plus de 5000 case . Quels sont les inconvénients et comment nous pouvons le remplacer par quelque chose de plus rapidement?

Remarque: je ne m'attends pas à utiliser une matrice pour stocker des cas car il est identique.


4 commentaires

Pourquoi voudriez-vous comparer 5000 cas? Vous ne pouvez pas comprimer / refactoriser le code afin que vous obteniez moins de cas dans chaque segment de code?


Vous pouvez utiliser une gamme de pointes de fonction , il est beaucoup de temps plus rapide que toute comparaison, mais ma question est ... Comment pouvez-vous Gérer et maintenir Code avec un interrupteur / cas instruction avec 5000 cas?


Si vous devez basculer entre 5000 cas, cela ressemble à une mauvaise intention de conception. Sinon, j'irais aussi pour les pointeurs de la fonction.


@mmoment: Le refactoring est définitivement la solution, mais je cherche une autre façon.


4 Réponses :


3
votes

Essayez d'utiliser la classe de dictionnaire avec des valeurs déléguées. Au moins cela rend le code un peu plus lisible.


1 commentaires

La seule exigence explicite de la question était "quelque chose de plus rapide" ... Une carte de hachage sera probablement significativement plus lente, de même que l'appel hors ligne forcé (qui est généralement un ordre de magnitude plus lentement que le code en ligne pour une seule opération triviale qui est l'endroit où la vitesse de commutation) surtout si les cas ne se répandent pas au hasard sur une large gamme.



13
votes

Il n'y a pas de raison spécifique de penser que vous voudriez autre chose qu'une déclaration d'interrupteur / cas (et j'attendais bien activement qu'elle soit inutile). Le compilateur doit créer un code de répartition efficace, ce qui pourrait impliquer une combinaison de table (s) statique [s) et d'indexation directe, de branchement binaire, etc. Il a des idées sur les valeurs statiques des cas et devrait faire un excellent travail (la rétablissant à la volée chaque fois que vous modifiez les cas, alors que de nouvelles valeurs qui ne correspondent pas bien à une approche fabriquée à la main - telles que des valeurs extrêmement différentes Lorsque vous auriez eu une jolie liste de gamme emballée, vous pourriez avoir besoin de retravailler du code ou de causer de la mémoire en silence ou une chute de performances).

Les gens se soucient vraiment de ce genre de chose de retour lorsque C essayait de gagner sur des programmeurs d'assemblage du dur ... Les compilateurs ont été tenus responsables de la génération d'un bon code. Mettez un autre moyen - si ce n'est pas (de manière mesurable) cassé, ne le réparez pas.

Plus généralement, il est bon d'être curieux de ce genre de chose et d'obtenir des idées des gens sur des alternatives et de leurs implications de la performance, mais si vous vraiment les soins et la différence de performance pourraient faire une différence utile pour votre Programme (surtout si le profilage le suggère), alors toujours une référence avec votre programme faisant du travail réel.


6 commentaires

Oui, je pense que le compilateur fera de l'essentiel, bien que 5 000 cas ne soient pas normaux. Un éventail de pointes de fonction est probablement meilleur, au moins le code sera meilleur.


@Mine: Le tableau du pointeur de fonction rendra le code lisible. Mais je cherche une solution plus rapide.


@MarsRover Gamme de pointeur de fonction est rapide. Une multiplication, une déférence un pointeur et un appel. Il est difficile d'avoir moins de instructions. De plus, je suis d'accord avec Tony, si la conception n'est pas un problème alors êtes-vous sûr que la performance souffre?


@Mine: 5000 cas ne sont pas normaux dans le code de niveau d'application typique, mais disent que vous écrivez un compilateur ou un système d'exploitation - ou un code de compilation produit par certains systèmes automatisés tels qu'un générateur de table de hachage parfait, vous pouvez raisonnablement s'attendre à ce que ce code soit exécuté comme foudre graissé. Selon mon commentaire sur la réponse de Rusty, l'utilisation des pointeurs de fonction au moment de l'exécution peut vraiment affecter négativement la performance, comme des appels hors ligne qui finissent par faire quelque chose de trivial (par exemple, le réglage / incrémentation d'un int ) peut facilement être un ordre de grandeur plus lent.


@Tontydelroy Si ce qu'il doit effectuer dans chaque cas, c'est vraiment trivial alors d'utiliser le code de montage comme des valeurs ENUM peuvent être sa meilleure solution (en tant que versions originales de la fonction BitBLT). S'il doit appeler une fonction plus complexe, je ne sais pas si la planification des instructions sera vraiment un problème pour lui.


L'un des problèmes est que le compilateur prenant énormément de temps pour compiler un énorme morceau de code dans un fichier unique.



9
votes

comme nourriture pour la pensée ... au cas où l'on pouvait être bloqué avec un compilateur ancien / buggy / inefficace ou juste l'amour piratage.

travaux interne de l'interrupteur code> est composé de deux parties. Trouver une adresse pour sauter et sauter bien là-bas. Pour la première partie, vous devez utiliser une table pour trouver l'adresse. Si le nombre de cas augmente, la table devient plus grande - la recherche d'une adresse à sauter prend du temps. Il s'agit que les compilateurs de points essaient d'optimiser, combinant plusieurs techniques, mais une approche facile consiste à utiliser la table directement, ce qui dépend de l'espace de la valeur de cas. P>

dans le dos de l'exemple de la serviette; P>

const void *cases[] = {&&case_foo, &&case_bar, &&case_baz, ....};
goto *labels[n];
case_foo:
    foo();
    goto switch_end;
case_bar:
    bar();
    goto switch_end;
case_baz:
    baz();
    goto switch_end;
// and a lot other
switch_end:


1 commentaires

Merci de réponse, cela ne fait pas partie de ma conception, mais quelqu'un m'a posé cette question et j'ai essayé de l'expliquer en utilisant un tableau de pointeur de fonction. Mais ce n'était ni un moyen rapide. Je cherchais moi-même un moyen plus rapide, je ne parle pas de la lisibilité ou du design ici.



1
votes

Énoncé de gros interrupteur, généralement généré automatiquement, peut prendre beaucoup de temps pour compiler. Mais j'aime l'idée que le compilateur optimise l'instruction de commutation.

Un moyen de séparer l'instruction de commutation consiste à utiliser le godetage, xxx

donc dans le code ci-dessus, nous a éclaté à l'écart de notre instruction de commutation en 16 parties. Il est facile de modifier le nombre de godets au code généré automatiquement.

Ce code a ajouté un coût d'exécution d'une couche d'indirection ou d'appel de fonction. . Mais compte tenu des godets définis dans différents fichiers, il est plus rapide de les compiler en parallèle.


0 commentaires