É importante não confundir a instrução C # switch com a instrução CIL switch.
O comutador CIL é uma tabela de salto, que requer um índice em um conjunto de endereços de salto.
Isso é útil apenas se os casos do comutador C # forem adjacentes:
case 3: blah; break;
case 4: blah; break;
case 5: blah; break;
Mas de pouca utilidade, se não forem:
case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;
(Você precisaria de uma tabela com cerca de 3000 entradas, com apenas 3 slots usados)
Com expressões não adjacentes, o compilador pode começar a executar verificações lineares se-se-se-se.
Com conjuntos de expressões não adjacentes maiores, o compilador pode começar com uma pesquisa em árvore binária e, finalmente, se-outro-se-outro nos últimos itens.
Com conjuntos de expressões que contêm grupos de itens adjacentes, o compilador pode pesquisar em árvore binária e, finalmente, uma opção CIL.
Está cheio de "mays" e "mights" e depende do compilador (pode ser diferente com Mono ou Rotor).
Eu repliquei seus resultados na minha máquina usando casos adjacentes:
tempo total para executar um comutador de 10 vias, 10000 iterações (ms): 25.1383
tempo aproximado por comutador de 10 vias (ms): 0,00251383
tempo total para executar um comutador de 50 direções, 10000 iterações (ms): 26,593
tempo aproximado por comutador de 50 direções (ms): 0,0026593
tempo total para executar um comutador de 5000 direções, 10000 iterações (ms): 23,7094
tempo aproximado por comutador de 5000 direções (ms): 0,00237094
tempo total para executar um comutador de 50000 vias, 10000 iterações (ms): 20,0933
tempo aproximado por comutador de 50000 vias (ms): 0,00200933
Então eu também fiz usando expressões de caso não adjacentes:
tempo total para executar um comutador de 10 vias, 10000 iterações (ms): 19,6189
tempo aproximado por comutador de 10 vias (ms): 0,00196189
tempo total para executar um comutador de 500 vias, 10000 iterações (ms): 19.1664
tempo aproximado por comutador de 500 vias (ms): 0,00191664
tempo total para executar um comutador de 5000 vias, 10000 iterações (ms): 19,5871
tempo aproximado por comutador de 5000 vias (ms): 0,00195871
Uma instrução de comutação de caso não adjacente de 50.000 não seria compilada.
"Uma expressão é muito longa ou complexa para compilar perto de 'ConsoleApplication1.Program.Main (string [])'
O engraçado aqui é que a pesquisa em árvore binária aparece um pouco (provavelmente não estatisticamente) mais rapidamente do que a instrução CIL switch.
Brian, você usou a palavra " constante ", que tem um significado muito definido do ponto de vista da teoria da complexidade computacional. Embora o exemplo inteiro adjacente simplista possa produzir CIL considerado O (1) (constante), um exemplo esparso é O (log n) (logarítmico), exemplos agrupados estão em algum lugar no meio e pequenos exemplos são O (n) (linear). )
Isso nem resolve a situação String, na qual uma estática Generic.Dictionary<string,int32>
pode ser criada, e sofrerá uma sobrecarga definida no primeiro uso. O desempenho aqui dependerá do desempenho de Generic.Dictionary
.
Se você verificar a especificação de idioma C # (não a especificação CIL), encontrará "15.7.2 A instrução switch" não menciona "tempo constante" ou que a implementação subjacente ainda usa a instrução de opção CIL (tenha muito cuidado em assumir tais coisas).
No final do dia, uma opção C # contra uma expressão inteira em um sistema moderno é uma operação de microssegundos e normalmente não vale a pena se preocupar.
É claro que esses tempos dependerão de máquinas e condições. Eu não prestaria atenção a esses testes de temporização, as durações de microssegundos que estamos falando são diminuídas por qualquer código "real" sendo executado (e você deve incluir algum "código real", caso contrário o compilador otimizará a ramificação), ou instabilidade no sistema. Minhas respostas são baseadas no uso do IL DASM para examinar o CIL criado pelo compilador C #. Obviamente, isso não é final, pois as instruções reais que a CPU executa são criadas pelo JIT.
Verifiquei as instruções finais da CPU realmente executadas na minha máquina x86 e posso confirmar um simples comutador de conjunto adjacente fazendo algo como:
jmp ds:300025F0[eax*4]
Onde uma pesquisa em árvore binária está cheia de:
cmp ebx, 79Eh
jg 3000352B
cmp ebx, 654h
jg 300032BB
…
cmp ebx, 0F82h
jz 30005EEE