Il est important de ne pas confondre l'instruction de commutateur C # avec l'instruction de commutateur CIL.
Le commutateur CIL est une table de sauts, 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 peu utiles s'ils ne le sont pas:
case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;
(Vous auriez besoin d'une table d'environ 3000 entrées en taille, avec seulement 3 emplacements utilisés)
Avec des expressions non adjacentes, le compilateur peut commencer à effectuer des vérifications linéaires if-else-if-else.
Avec des ensembles d'expressions non adjacents plus grands, le compilateur peut commencer par une recherche d'arborescence 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 rechercher dans l'arborescence 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 répliqué vos résultats sur ma machine en utilisant des cas adjacents:
temps total pour exécuter un commutateur 10 voies, 10000 itérations (ms): 25,1383
temps approximatif par commutateur 10 voies (ms): 0,00251383
temps total pour exécuter un commutateur à 50 voies, 10000 itérations (ms): 26,593
temps approximatif par commutateur à 50 voies (ms): 0,0026593
temps total pour exécuter un commutateur à 5000 voies, 10000 itérations (ms): 23,7094
temps approximatif par commutateur à 5000 voies (ms): 0,00237094
temps total pour exécuter un commutateur de 50000 voies, 10000 itérations (ms): 20,0933
temps approximatif par commutateur de 50000 voies (ms): 0,00200933
Ensuite, j'ai également utilisé des expressions de cas non adjacentes:
temps total pour exécuter un commutateur 10 voies, 10000 itérations (ms): 19,6189
temps approximatif par commutateur 10 voies (ms): 0,00196189
temps total pour exécuter un commutateur à 500 voies, 10000 itérations (ms): 19,1664
temps approximatif par commutateur à 500 voies (ms): 0,00191664
temps total pour exécuter un commutateur à 5000 voies, 10000 itérations (ms): 19,5871
temps approximatif par commutateur à 5000 voies (ms): 0,00195871
Une instruction switch de 50 000 cas non adjacente ne serait pas compilée.
"Une expression est trop longue ou trop complexe pour être compilée près de 'ConsoleApplication1.Program.Main (string [])'
Ce qui est drôle ici, c'est que la recherche dans l'arbre binaire apparaît un peu (probablement pas statistiquement) plus rapidement que l'instruction de commutation CIL.
Brian, vous avez utilisé le mot « constante », qui a une signification très précise du point de vue de la théorie de la complexité computationnelle. Alors que l'exemple d'entier adjacent simpliste peut produire un CIL considéré comme O (1) (constant), un exemple fragmenté est O (log n) (logarithmique), les exemples groupés se trouvent quelque part entre les deux et les petits exemples sont O (n) (linéaire ).
Cela ne résout même pas la situation de String, dans laquelle une statique Generic.Dictionary<string,int32>
peut être créée, et subira une surcharge définie lors de la première utilisation. La performance dépendra ici de la performance de Generic.Dictionary
.
Si vous vérifiez la spécification du langage C # (pas la spécification CIL), vous trouverez "15.7.2 L'instruction switch" ne fait aucune mention du "temps constant" ou que l'implémentation sous-jacente utilise même l'instruction switch CIL (soyez très prudent de supposer de telles choses).
À la fin de la journée, un commutateur C # contre une expression entière sur un système moderne est une opération inférieure à la microseconde et ne vaut normalement pas la peine de s'inquiéter.
Bien entendu, ces délais dépendront des machines et des conditions. Je ne ferais pas attention à ces tests de synchronisation, les durées en microsecondes dont nous parlons sont éclipsées par tout code «réel» exécuté (et vous devez inclure du «code réel» sinon le compilateur optimisera la branche), ou gigue dans le système. Mes réponses sont basées sur l'utilisation d' 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 exécutées par le processeur sont ensuite créées par le JIT.
J'ai vérifié les instructions du processeur finales réellement exécutées sur ma machine x86 et je peux confirmer un simple commutateur de jeu adjacent faisant quelque chose comme:
jmp ds:300025F0[eax*4]
Où une recherche d'arbre binaire est pleine de:
cmp ebx, 79Eh
jg 3000352B
cmp ebx, 654h
jg 300032BB
…
cmp ebx, 0F82h
jz 30005EEE