Ważne jest, aby nie mylić instrukcji przełącznika C # z instrukcją przełącznika CIL.
Przełącznik CIL to tablica skoku, która wymaga indeksu w zestawie adresów skoku.
Jest to przydatne tylko wtedy, gdy przypadki przełącznika C # są sąsiadujące:
case 3: blah; break;
case 4: blah; break;
case 5: blah; break;
Ale mało przydatne, jeśli nie są:
case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;
(Potrzebowałbyś tabeli o rozmiarze ~ 3000 wpisów, z użyciem tylko 3 gniazd)
W przypadku wyrażeń niesąsiadujących kompilator może rozpocząć wykonywanie liniowych kontroli if-else-if-else.
W przypadku większych, niesąsiadujących ze sobą zestawów wyrażeń kompilator może rozpocząć od przeszukiwania drzewa binarnego, a na końcu if-else-if-else kilku ostatnich elementów.
W przypadku zestawów wyrażeń zawierających grupy sąsiednich elementów, kompilator może przeszukiwać drzewo binarne, a na końcu przełączać CIL.
Jest to pełne "majów" i "potęg" i jest zależne od kompilatora (może się różnić w przypadku Mono lub Rotora).
Zreplikowałem twoje wyniki na moim komputerze przy użyciu sąsiednich przypadków:
całkowity czas wykonania 10-pozycyjnego przełącznika, 10000 iteracji (ms): 25,1383
przybliżony czas na 10-pozycyjny przełącznik (ms): 0,00251383
całkowity czas wykonania 50-stykowego przełącznika, 10000 iteracji (ms): 26,593
przybliżony czas na 50-kierunkowy przełącznik (ms): 0,0026593
całkowity czas do wykonania przełącznika z 5000 kierunków, 10000 iteracji (ms): 23,7094
przybliżony czas na przełączenie z 5000 kierunków (ms): 0,00237094
całkowity czas do wykonania przełącznika 50000, 10000 iteracji (ms): 20,0933
przybliżony czas na 50000 przełącznika kierunkowego (ms): 0,00200933
Następnie użyłem również nieprzylegających wyrażeń wielkości liter:
całkowity czas wykonania 10-pozycyjnego przełącznika, 10000 iteracji (ms): 19,6189
przybliżony czas na 10-pozycyjny przełącznik (ms): 0,00196189
całkowity czas wykonania 500-kierunkowego przełącznika, 10000 iteracji (ms): 19,1664
przybliżony czas na 500-kierunkowy przełącznik (ms): 0,00191664
całkowity czas do wykonania przełącznika z 5000 kierunków, 10000 iteracji (ms): 19,5871
przybliżony czas na przełączenie z 5000 kierunków (ms): 0,00195871
Nie sąsiadująca instrukcja przełączania wielkości liter 50 000 nie zostanie skompilowana.
„Wyrażenie jest zbyt długie lub zbyt złożone, aby można je było skompilować w pobliżu„ ConsoleApplication1.Program.Main (string []) ”
Zabawne jest to, że przeszukiwanie drzewa binarnego pojawia się trochę (prawdopodobnie nie statystycznie) szybciej niż instrukcja przełącznika CIL.
Brian, użyłeś słowa „ stała ”, które ma bardzo określone znaczenie z perspektywy teorii złożoności obliczeniowej. Podczas gdy uproszczony przykład sąsiedniej liczby całkowitej może dać CIL, który jest uważany za O (1) (stała), rzadki przykład to O (log n) (logarytmiczny), przykłady skupione znajdują się gdzieś pomiędzy, a małe przykłady to O (n) (liniowe ).
Nie rozwiązuje to nawet sytuacji typu String, w której Generic.Dictionary<string,int32>
może zostać utworzony statyczny , a przy pierwszym użyciu będzie to miało określony narzut. Wydajność w tym miejscu będzie zależna od wydajności Generic.Dictionary
.
Jeśli sprawdzisz specyfikację języka C # (nie specyfikację CIL), zobaczysz "15.7.2 Instrukcja switch" nie wspomina o "stałym czasie" lub że podstawowa implementacja używa nawet instrukcji przełącznika CIL (uważaj przy założeniu takie rzeczy).
Pod koniec dnia przełączenie C # na wyrażenie całkowite w nowoczesnym systemie jest operacją poniżej mikrosekundy i zwykle nie warto się tym martwić.
Oczywiście te czasy będą zależeć od maszyn i warunków. Nie zwracałbym uwagi na te testy czasowe, czas trwania w mikrosekundach, o którym mówimy, jest przyćmiony przez każdy uruchamiany „prawdziwy” kod (i musisz dołączyć jakiś „prawdziwy kod”, w przeciwnym razie kompilator zoptymalizuje gałąź) lub jitter w systemie. Moje odpowiedzi opierają się na używaniu IL DASM do badania CIL utworzonego przez kompilator C #. Oczywiście nie jest to ostateczne, ponieważ JIT tworzy instrukcje, które uruchamia procesor.
Sprawdziłem końcowe instrukcje procesora faktycznie wykonywane na mojej maszynie x86 i mogę potwierdzić prosty sąsiedni przełącznik zestawu, wykonujący coś takiego:
jmp ds:300025F0[eax*4]
Gdzie wyszukiwanie drzewa binarnego jest pełne:
cmp ebx, 79Eh
jg 3000352B
cmp ebx, 654h
jg 300032BB
…
cmp ebx, 0F82h
jz 30005EEE