Jaka jest korzyść / wada używania switch
wyciągu w porównaniu do an if/else
w C #. Nie mogę sobie wyobrazić, żeby istniała tak duża różnica, inna niż może wygląd twojego kodu.
Czy jest jakiś powód, dla którego wynikowa wydajność IL lub powiązane środowisko uruchomieniowe byłoby diametralnie różne?
Powiązane: Co jest szybsze, przełącza ciąg lub na inny typ?
c#
.net
switch-statement
Matthew M. Osborn
źródło
źródło
Odpowiedzi:
Instrukcja SWITCH produkuje tylko ten sam zestaw co IFs w trybie debugowania lub zgodności. W wydaniu zostanie on skompilowany do tabeli skoków (za pomocą instrukcji MSIL „switch”) - czyli O (1).
C # (w przeciwieństwie do wielu innych języków) pozwala również na włączenie stałych ciągów - i działa to nieco inaczej. Oczywiście nie jest praktyczne budowanie tabel skoków dla ciągów o dowolnej długości, więc najczęściej taki przełącznik będzie kompilowany w stos IF.
Ale jeśli liczba warunków jest wystarczająco duża, aby pokryć koszty ogólne, kompilator C # utworzy obiekt HashTable, zapełni go stałymi ciągami i przeprowadzi wyszukiwanie w tej tabeli, a następnie skok. Hashtable lookup nie jest ściśle O (1) i ma zauważalne stałe koszty, ale jeśli liczba etykiet wielkości liter jest duża, będzie to znacznie szybsze niż w porównaniu do każdej stałej łańcucha w IF.
Podsumowując, jeśli liczba warunków jest większa niż 5, wybierz SWITCH zamiast IF, w przeciwnym razie użyj tego, co wygląda lepiej.
źródło
Ogólnie (biorąc pod uwagę wszystkie języki i wszystkie kompilatory) instrukcja switch MOŻE CZASEM być bardziej wydajna niż instrukcja if / else, ponieważ kompilator łatwo generuje tabele skoku z instrukcji switch. Można zrobić to samo dla instrukcji if / else z odpowiednimi ograniczeniami, ale jest to o wiele trudniejsze.
W przypadku C # jest to również prawdą, ale z innych powodów.
Przy dużej liczbie ciągów znacząca przewaga wydajności polega na użyciu instrukcji switch, ponieważ kompilator użyje tabeli skrótów do wdrożenia skoku.
Przy niewielkiej liczbie ciągów wydajność między nimi jest taka sama.
Jest tak, ponieważ w takim przypadku kompilator C # nie generuje tabeli skoków. Zamiast tego generuje MSIL, który jest równoważny blokom IF / ELSE.
Istnieje instrukcja MSIL „instrukcja przełączania”, która po wysłaniu użyje tabeli skoków do implementacji instrukcji przełączania. Działa jednak tylko z typami liczb całkowitych (to pytanie dotyczy ciągów znaków).
W przypadku niewielkiej liczby łańcuchów kompilator bardziej efektywnie generuje bloki IF / ELSE, niż korzysta z tabeli skrótów.
Kiedy pierwotnie to zauważyłem, założyłem, że ponieważ bloki IF / ELSE były używane z małą liczbą łańcuchów, to kompilator dokonał tej samej transformacji dla dużej liczby łańcuchów.
To było ŹLE. „IMA” był na tyle uprzejmy, aby mi to wskazać (cóż ... nie był do tego miły, ale miał rację, a ja się myliłem, co jest ważną częścią)
Przyjąłem również założenie, że brak instrukcji „przełączania” w MSIL (pomyślałem, że jeśli istnieje prymityw przełącznika, to dlaczego nie używają go z tabelą skrótów, więc nie może istnieć prymityw przełącznika). ...) To było zarówno złe, jak i niesamowicie głupie z mojej strony. „IMA” ponownie mi to wskazało.
Dokonałem tutaj aktualizacji, ponieważ jest to najwyżej oceniany post i zaakceptowana odpowiedź.
Zrobiłem to jednak Wiki Wiki, ponieważ uważam, że nie zasługuję na REP za to, że się mylę. Jeśli masz szansę, zagłosuj na wpis „ima”.
źródło
Trzy powody, dla których wolisz
switch
:Kompilator celujący w natywny kod często kompiluje instrukcję switch w jedną gałąź warunkową plus skok pośredni, podczas gdy sekwencja
if
s wymaga sekwencji gałęzi warunkowych . W zależności od gęstości przypadków napisano bardzo wiele wyuczonych artykułów na temat efektywnego kompilowania zestawień przypadków; niektóre są połączone ze strony kompilatora lcc . (Lcc miał jeden z bardziej innowacyjnych kompilatorów do przełączników).Instrukcja switch jest wyborem spośród wzajemnie wykluczających się alternatyw, a składnia przełącznika czyni ten przepływ sterowania bardziej przejrzystym dla programisty niż gniazdo instrukcji if-then-else.
W niektórych językach, w tym zdecydowanie ML i Haskell, kompilator sprawdza, czy pominięto jakieś przypadki . Uważam tę funkcję za jedną z głównych zalet ML i Haskell. Nie wiem czy C # może to zrobić.
Anegdota: na wykładzie, który wygłosił po otrzymaniu nagrody za całokształt twórczości, usłyszałem, jak Tony Hoare powiedział, że ze wszystkich rzeczy, które zrobił w swojej karierze, były trzy, z których był najbardziej dumny:
case
instrukcją)Nie wyobrażam sobie życia bez
switch
.źródło
Kompilator zoptymalizuje prawie wszystko do tego samego kodu z niewielkimi różnicami (Knuth, ktoś?).
Różnica polega na tym, że instrukcja switch jest czystsza niż piętnaście, jeśli w przeciwnym razie zestawione są razem.
Znajomi nie pozwalają znajomym układać instrukcji if-else.
źródło
W rzeczywistości instrukcja switch jest bardziej wydajna. Kompilator zoptymalizuje go do tabeli wyszukiwania, w której nie można tego zrobić za pomocą instrukcji if / else. Wadą jest to, że instrukcja switch nie może być używana z wartościami zmiennymi.
Nie możesz zrobić:
to musi być
źródło
Nie widziałem, aby ktokolwiek podniósł (oczywisty?) Punkt, że domniemana przewaga wydajności instrukcji switch zależy od tego, czy różne przypadki są w przybliżeniu jednakowo prawdopodobne. W przypadkach, w których jedna (lub kilka) wartości jest znacznie bardziej prawdopodobna, drabina „jeśli-to-inaczej” może być znacznie szybsza, zapewniając najpierw sprawdzenie najczęstszych przypadków:
Na przykład:
vs
Jeśli x wynosi zero w 90% przypadków, kod „jeśli-inaczej” może być dwa razy szybszy niż kod oparty na przełączaniu. Nawet jeśli kompilator zmieni „przełącznik” w coś w rodzaju sprytnego goto sterowanego tabelą, nadal nie będzie tak szybki, jak po prostu sprawdzanie zera.
źródło
switch
kompatybilne,switch
instrukcja jest lepsza (bardziej czytelna, czasem szybsza). Jeśli wiesz, że jeden przypadek jest znacznie bardziej prawdopodobny, możesz go wyciągnąć, aby utworzyć konstrukcjęif
-else
-switch
i jeśli jest to wymiernie szybsze , zostaw to w. (Powtórz, jeśli to konieczne.) IMO jest nadal dość czytelne. Jeśliswitch
degeneruje się i staje się zbyt mały, zastąpienie wyrażenia regularnego wykona większość pracy przekształcania go welse if
łańcuch.często będzie wyglądać lepiej - tzn. łatwiej będzie zrozumieć, co się dzieje. Biorąc pod uwagę, że poprawa wydajności będzie co najwyżej wyjątkowo minimalna, najważniejszą różnicą jest widok kodu.
Tak więc, jeśli if / else wygląda lepiej, użyj go, w przeciwnym razie użyj instrukcji switch.
źródło
Temat poboczny, ale często martwię się (i częściej widzę)
if
/else
iswitch
instrukcja staje się zbyt duża w zbyt wielu przypadkach. Często przeszkadzają w utrzymaniu.Powszechni sprawcy to:
Naprawić:
źródło
Zgodnie z tym linkiem porównanie testu iteracji IF vs Switch za pomocą instrukcji switch i if jest jak dla 1 000 000 000 iteracji, czas potrzebny na instrukcję Switch = 43.0s i przez If If = 48.0s
To jest dosłownie 20833333 iteracji na sekundę, więc czy naprawdę powinniśmy się bardziej skoncentrować,
PS: Aby poznać różnicę wydajności dla małej listy warunków.
źródło
Jeśli używasz tylko instrukcji if lub else, podstawowe rozwiązanie używa porównania? operator
Możesz zrobić lub rutynowo w przełączniku
źródło
To tak naprawdę nie odpowiada na twoje pytanie, ale biorąc pod uwagę, że między skompilowanymi wersjami będzie niewielka różnica, zachęcam do napisania kodu w sposób, który najlepiej opisuje twoje zamiary. Nie tylko jest większa szansa, że kompilator zrobi to, czego oczekujesz, ale ułatwi innym utrzymanie Twojego kodu.
Jeśli twoim zamiarem jest rozgałęzienie programu na podstawie wartości jednej zmiennej / atrybutu, wówczas instrukcja switch najlepiej reprezentuje ten zamiar.
Jeśli Twoim celem jest rozgałęzienie programu w oparciu o różne zmienne / atrybuty / warunki, to łańcuch if / else if najlepiej reprezentuje ten zamiar.
Przyznaję, że cody ma rację, że ludzie zapominają o komendzie break, ale prawie tak często widzę, że ludzie robią skomplikowane, jeśli bloki, w których źle {} się mylą, więc wiersze, które powinny znajdować się w instrukcji warunkowej, nie są. Jest to jeden z powodów, dla których zawsze umieszczam {} w moich instrukcjach if, nawet jeśli jest w nich jedna linia. Nie tylko łatwiej jest czytać, ale jeśli muszę dodać kolejną linię warunkową, nie mogę zapomnieć o jej dodaniu.
źródło
Pytanie o zainteresowanie To pojawiło się kilka tygodni temu w pracy i znaleźliśmy odpowiedź, pisząc przykładowy fragment i przeglądając go w .NET Reflector (reflektor jest niesamowity !! uwielbiam go).
Oto, co odkryliśmy: Poprawna instrukcja switch dla czegoś innego niż ciąg jest kompilowana do IL jako instrukcja switch. Jednak JEŻELI jest to ciąg znaków, jest przepisywany jako if / else if / else w IL. W naszym przypadku chcieliśmy wiedzieć, w jaki sposób instrukcje switch porównują ciągi znaków, np. Rozróżnia wielkość liter itp., A reflektor szybko dał nam odpowiedź. Warto było to wiedzieć.
Jeśli chcesz zrobić rozróżniana porównać na sznurkach wtedy mógłby użyć instrukcji switch, gdyż jest szybszy niż wykonywanie String.Compare w if / else. (Edycja: Czytaj, co jest szybsze, włącz ciąg lub ifif na typ? W przypadku niektórych faktycznych testów wydajności) Jednak jeśli chcesz zrobić bez rozróżniania wielkości liter, lepiej jest użyć if / else, ponieważ wynikowy kod nie jest ładny.
Najlepszą ogólną zasadą jest używanie instrukcji switch, jeśli ma to sens (poważnie), np .:
Jeśli potrzebujesz manipulować wartością, aby wprowadzić ją do instrukcji switch (utwórz zmienną tymczasową, na którą chcesz się przełączyć), prawdopodobnie powinieneś użyć instrukcji kontrolnej if / else.
Aktualizacja:
Właściwie lepiej jest przekonwertować ciąg na wielkie litery (np.
ToUpper()
), Ponieważ najwyraźniej istnieją dalsze optymalizacje, które kompilator just-in-time może zrobić w porównaniu zToLower()
. Jest to mikrooptymalizacja, jednak w ciasnej pętli może się przydać.Mała uwaga dodatkowa:
Aby poprawić czytelność instrukcji switch, spróbuj:
źródło
Instrukcja switch jest zdecydowanie szybsza niż if if else. Istnieje test prędkości dostarczony na nim przez BlackWasp
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
--Sprawdź to
Ale zależy w dużej mierze od możliwości, które próbujesz uwzględnić, ale staram się używać instrukcji switch, gdy tylko jest to możliwe.
źródło
Myślę, że nie tylko język C #, ale wszystkie języki oparte na języku C: ponieważ przełącznik ogranicza się do stałych, możliwe jest wygenerowanie bardzo wydajnego kodu przy użyciu „tablicy skoków”. Przypadek C jest naprawdę dobrym, starym obliczonym GOTO przez FORTRAN, ale przypadek C # wciąż testuje względem stałej.
Nie jest tak, że optymalizator będzie mógł stworzyć ten sam kod. Rozważ np.
Ponieważ są to złożone wartości logiczne, wygenerowany kod musi obliczyć wartość i skrót. Teraz rozważ odpowiednik
Można to skompilować
ponieważ domyślnie informujesz kompilator, że nie musi on obliczać testów OR i testów równości.
źródło
Mój profesor cs zasugerował, aby nie zmieniać wypowiedzi, ponieważ tak często ludzie zapominali o przerwie lub używali jej nieprawidłowo. Nie mogę sobie przypomnieć dokładnie tego, co powiedział, ale coś podobnego do tego, że patrząc na niektóre podstawowe kody, które pokazały przykłady instrukcji switch (lata temu), zawierało w sobie mnóstwo błędów.
źródło
Coś, co właśnie zauważyłem, to możliwość łączenia instrukcji if / else i przełączania instrukcji! Bardzo przydatne, gdy trzeba sprawdzić warunki wstępne.
źródło
Myślę, że zmiana jest szybsza niż wtedy, gdy warunki takie jak sprawdzają, czy istnieje program taki jak:
Napisz program, aby wprowadzić dowolną liczbę (od 1 do 99) i sprawdź, w której szczelinie a) 1 - 9, następnie 1) b - 11 - 19, a następnie 2 c) 21-29, a następnie 3 i tak dalej aż do 89- 99
Następnie włącz, jeśli musisz stworzyć wiele warunków, ale skrzynka przełącznika syna musisz po prostu wpisać
będzie tak łatwo
Istnieje również wiele innych takich przykładów!
źródło
instrukcja switch jest w zasadzie porównaniem równości. Zdarzenie na klawiaturze ma wielką przewagę nad instrukcją switch, gdy ma łatwość pisania i odczytywania kodu, a wtedy instrukcja if elseif, brakując {nawiasu}, również może sprawiać problemy.
Instrukcja if elseif jest idealna dla więcej niż jednego rozwiązania, jeśli (AmountOfApples jest większa niż 5 i & AmountOfApples jest mniejsza niż 10) zapisz swoje jabłka, jeśli (AmountOfApples jest większe niż 10 || AmountOfApples == 100) sprzedaj jabłka. Nie piszę w języku c # lub c ++, ale nauczyłem się tego, zanim nauczyłem się języka Java i są to bliskie języki.
źródło
Jednym z możliwych wad instrukcji switch jest brak wielu warunków. Możesz mieć wiele warunków dla instrukcji if (else), ale nie wiele instrukcji z różnymi warunkami w przełączniku.
Instrukcje switch nie nadają się do operacji logicznych wykraczających poza proste równania / wyrażenia logiczne. Dla tych równań / wyrażeń logicznych jest on szczególnie odpowiedni, ale nie do innych operacji logicznych.
Masz dużo większą swobodę dzięki logice dostępnej w instrukcjach If, ale czytelność może ucierpieć, jeśli instrukcja If stanie się nieporęczna lub będzie źle obsługiwana.
Oba mają miejsce w zależności od kontekstu tego, z czym masz do czynienia.
źródło