Il est important de ne pas confondre l'instruction switch C# avec l'instruction switch CIL.
Le commutateur CIL est une table de saut, qui nécessite un index dans un ensemble d'adresses de saut.
Ceci n'est utile que si les cas du commutateur C# sont adjacents :
case 3: blah; break;
case 4: blah; break;
case 5: blah; break;
Mais ça ne sert pas à grand-chose s'ils ne le sont pas :
case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;
(Vous auriez besoin d'une table de ~3000 entrées, avec seulement 3 slots utilisés).
Avec des expressions non adjacentes, le compilateur peut commencer à effectuer des vérifications linéaires if-else-if-else.
Avec de plus grands ensembles d'expressions non adjacentes, le compilateur peut commencer par une recherche dans un arbre binaire, et finalement if-else-if-else les derniers éléments.
Avec des ensembles d'expressions contenant des groupes d'éléments adjacents, le compilateur peut effectuer une recherche d'arbre binaire, et enfin un commutateur CIL.
C'est plein de "mays" et "mights", et cela dépend du compilateur (peut différer avec Mono ou Rotor).
J'ai reproduit vos résultats sur ma machine en utilisant des cas adjacents :
temps total pour exécuter un interrupteur à 10 voies, 10000 itérations (ms) : 25.1383
temps approximatif par interrupteur à 10 voies (ms) : 0.00251383
temps total pour exécuter un interrupteur à 50 voies, 10000 itérations (ms) : 26.593
temps approximatif par interrupteur à 50 voies (ms) : 0.0026593
temps total pour exécuter un interrupteur à 5000 voies, 10000 itérations (ms) : 23.7094
temps approximatif par interrupteur 5000 voies (ms) : 0.00237094
temps total pour exécuter un commutateur de 50000 voies, 10000 itérations (ms) : 20.0933
temps approximatif pour un interrupteur de 50000 voies (ms) : 0.00200933
Ensuite, j'ai également utilisé des expressions de cas non adjacents :
temps total pour exécuter un interrupteur à 10 voies, 10000 itérations (ms) : 19.6189
temps approximatif par interrupteur à 10 voies (ms) : 0.00196189
temps total pour exécuter un interrupteur à 500 voies, 10000 itérations (ms) : 19.1664
temps approximatif par interrupteur de 500 voies (ms) : 0.00191664
temps total pour exécuter un interrupteur à 5000 voies, 10000 itérations (ms) : 19.5871
temps approximatif par interrupteur 5000 voies (ms) : 0.00195871
Une instruction switch case non adjacente de 50 000 ne serait pas compilée.
"Une expression est trop longue ou trop complexe pour être compilée à proximité de 'ConsoleApplication1.Program.Main(string[])'.
Ce qui est amusant ici, c'est que la recherche dans l'arbre binaire semble un peu plus rapide (probablement pas statistiquement) que l'instruction de commutation CIL.
Brian, tu as utilisé le mot " constant "Ce terme a une signification très précise du point de vue de la théorie de la complexité informatique. Alors que l'exemple simpliste d'un nombre entier adjacent peut produire un CIL considéré comme O(1) (constant), un exemple clairsemé est O(log n) (logarithmique), les exemples groupés se situent quelque part entre les deux et les petits exemples sont O(n) (linéaires).
Cela n'aborde même pas la situation de String, dans laquelle un statique Generic.Dictionary<string,int32>
peuvent être créés, et subiront une surcharge certaine lors de leur première utilisation. La performance ici dépendra de la performance de Generic.Dictionary
.
Si vous vérifiez le Spécification du langage C# (pas la spécification CIL) vous trouverez que "15.7.2 L'instruction switch" ne mentionne pas de "temps constant" ou que l'implémentation sous-jacente utilise même l'instruction switch du CIL (soyez très prudent lorsque vous supposez de telles choses).
En fin de compte, une commutation C# par rapport à une expression entière sur un système moderne est une opération de l'ordre de la microseconde, et ne mérite normalement pas qu'on s'en préoccupe.
Bien entendu, ces délais dépendent des machines et des conditions. Je ne prêterais pas attention à ces tests de timing, les durées de quelques microsecondes dont nous parlons sont éclipsées par tout code "réel" en cours d'exécution (et vous devez inclure du "code réel", sinon le compilateur optimisera la branche), ou par la gigue du système. Mes réponses sont basées sur l'utilisation de IL DASM pour examiner le CIL créé par le compilateur C#. Bien sûr, ce n'est pas définitif, car les instructions réelles que le CPU exécute sont ensuite créées par le JIT.
J'ai vérifié les instructions finales du CPU réellement exécutées sur ma machine x86, et je peux confirmer qu'un simple interrupteur adjacent fait quelque chose comme ça :
jmp ds:300025F0[eax*4]
Là où une recherche par arbre binaire est pleine de :
cmp ebx, 79Eh
jg 3000352B
cmp ebx, 654h
jg 300032BB
…
cmp ebx, 0F82h
jz 30005EEE
1 votes
Voir la solution de contournement possible Existe-t-il une meilleure alternative que celle-ci pour "allumer le type" ?
0 votes
Une autre option pour activer les types intégrés est d'utiliser la fonction TypeCode Enum .
0 votes
Il suffit de créer un ENUM et d'utiliser NameOf dans Switch case. Cela fonctionnera comme une constante sur une variable dynamique.