È importante non confondere l'istruzione switch C # con l'istruzione switch CIL.
L'opzione CIL è una tabella di salto, che richiede un indice in una serie di indirizzi di salto.
Ciò è utile solo se i casi dell'interruttore C # sono adiacenti:
case 3: blah; break;
case 4: blah; break;
case 5: blah; break;
Ma di scarsa utilità se non lo sono:
case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;
(Avresti bisogno di una tabella di dimensioni ~ 3000 voci, con solo 3 slot utilizzati)
Con espressioni non adiacenti, il compilatore può iniziare a eseguire controlli if-else-if-else lineari.
Con insiemi di espressioni non adiacenti più grandi, il compilatore può iniziare con una ricerca ad albero binaria e infine if-else-if-else gli ultimi elementi.
Con insiemi di espressioni contenenti gruppi di elementi adiacenti, il compilatore può cercare l'albero binario e infine un interruttore CIL.
Questo è pieno di "mays" e "mights", e dipende dal compilatore (può differire con Mono o Rotor).
Ho replicato i tuoi risultati sulla mia macchina usando casi adiacenti:
tempo totale per eseguire un interruttore a 10 vie, 10000 iterazioni (ms): 25.1383
tempo approssimativo per interruttore a 10 vie (ms): 0.00251383
tempo totale per eseguire un interruttore a 50 vie, 10000 iterazioni (ms): 26.593
tempo approssimativo per interruttore a 50 vie (ms): 0.0026593
tempo totale per eseguire un interruttore a 5000 vie, 10000 iterazioni (ms): 23.7094
tempo approssimativo per interruttore a 5000 vie (ms): 0.00237094
tempo totale per eseguire un interruttore a 50000 vie, 10000 iterazioni (ms): 20.0933
tempo approssimativo per interruttore a 50000 vie (ms): 0.00200933
Quindi ho anche usato espressioni case non adiacenti:
tempo totale per eseguire un interruttore a 10 vie, 10000 iterazioni (ms): 19.6189
tempo approssimativo per interruttore a 10 vie (ms): 0.00196189
tempo totale per eseguire un interruttore a 500 vie, 10000 iterazioni (ms): 19.1664
tempo approssimativo per interruttore a 500 vie (ms): 0.00191664
tempo totale per eseguire un interruttore a 5000 vie, 10000 iterazioni (ms): 19.5871
tempo approssimativo per interruttore a 5000 vie (ms): 0.00195871
Non verrà compilata un'istruzione switch di caso 50.000 non adiacente.
"Un'espressione è troppo lunga o complessa per essere compilata vicino a" ConsoleApplication1.Program.Main (string []) "
La cosa divertente qui è che la ricerca dell'albero binario appare un po '(probabilmente non statisticamente) più veloce dell'istruzione switch CIL.
Brian, hai usato la parola " costante ", che ha un significato ben definito dal punto di vista della teoria della complessità computazionale. Mentre l'esempio intero adiacente semplicistico può produrre CIL considerato O (1) (costante), un esempio scarso è O (log n) (logaritmico), esempi raggruppati si trovano da qualche parte nel mezzo e piccoli esempi sono O (n) (lineare ).
Ciò non risolve nemmeno la situazione di String, in cui è Generic.Dictionary<string,int32>
possibile creare una statica e subirà un sovraccarico definito al primo utilizzo. Le prestazioni qui dipenderanno dalle prestazioni di Generic.Dictionary
.
Se controlli la specifica del linguaggio C # (non la specifica CIL) troverai "15.7.2 L'istruzione switch" non fa menzione di "tempo costante" o che l'implementazione sottostante utilizza anche l'istruzione switch CIL (fai molta attenzione a supporre tali cose).
Alla fine della giornata, un passaggio C # contro un'espressione intera su un sistema moderno è un'operazione sub-microseconda, e di solito non vale la pena preoccuparsi.
Naturalmente questi tempi dipenderanno dalle macchine e dalle condizioni. Non presterei attenzione a questi test di temporizzazione, le durate dei microsecondi di cui stiamo parlando sono sminuite da qualsiasi codice "reale" in esecuzione (e devi includere un "codice reale" altrimenti il compilatore ottimizzerà il ramo di distanza), oppure jitter nel sistema. Le mie risposte si basano sull'utilizzo di IL DASM per esaminare il CIL creato dal compilatore C #. Naturalmente questo non è definitivo, poiché le istruzioni effettive eseguite dalla CPU vengono quindi create da JIT.
Ho verificato le istruzioni finali della CPU effettivamente eseguite sulla mia macchina x86 e posso confermare un semplice switch adiacente facendo qualcosa del tipo:
jmp ds:300025F0[eax*4]
Dove una ricerca ad albero binario è piena di:
cmp ebx, 79Eh
jg 3000352B
cmp ebx, 654h
jg 300032BB
…
cmp ebx, 0F82h
jz 30005EEE