2010-05-12 8 views
17

powiedzmy mam switch jak poniżejCzy kolejność spraw w instrukcji Switch może różnić się wydajnością?

switch(alphabet) { 

    case "f": 
     //do something 
     break; 

    case "c": 
     //do something 
     break; 

    case "a": 
     //do something 
     break; 

    case "e": 
     //do something 
     break; 

} 

Załóżmy teraz wiem, że częstotliwość konieczności Alphabet E jest najwyższa, a następnie A, C i F odpowiednio. Tak, właśnie dokonał restrukturyzacji case oświadczenie porządek i uczynił je w następujący sposób:

switch(alphabet) { 

    case "e": 
     //do something 
     break; 

    case "a": 
     //do something 
     break; 

    case "c": 
     //do something 
     break; 

    case "f": 
     //do something 
     break; 
} 

Będzie druga switch oświadczenie szybciej niż pierwszy switch stwierdzeniem? Jeśli tak i jeśli w moim programie muszę wywołać to oświadczenie switch powiedzieć wiele razy, czy będzie to znacząca poprawa? A jeśli nie, w jaki sposób mogę wykorzystać moją wiedzę o częstotliwości, aby poprawić wydajność?

+3

Czy nie powinieneś po prostu napisać najczystszy sposób, w jaki wiesz, w jaki sposób, profil ze scenariuszami w świecie rzeczywistym i zoptymalizować tam, gdzie jest to potrzebne? –

Odpowiedz

20

Nie tak bardzo, że należy się tym martwić. Z pewnością nie jest to coś, co można przewidzieć.

W przypadku etykiet łańcuchów ciągów kompilator faktycznie używa wewnętrznej tabeli mieszania, która mapuje łańcuchy do indeksów w tabeli skoku. Tak więc operacja jest faktycznie O (1) - niezależnie od liczby etykiet.

Dla etykiet całkowitych, uważam, że faktyczny kod, który jest generowany, zależy od liczby etykiet i od tego, czy liczby są kolejne (lub "prawie" następujące po sobie). Jeśli są one kolejne (1, 2, 3, 4, ...), to po prostu zostaną przekształcone w tabelę skoku. Jeśli jest ich dużo, użyjemy tabeli skoku Hashtable + (podobnie jak w przypadku łańcuchów). Jeśli jest tylko kilka etykiet i nie są one tabelami, które mają zostać natychmiast przekształcone w tabelę skoków, tylko wtedy zostanie przekształcony w serię instrukcji if..then..else.

Generalnie jednak należy napisać kod, aby można było go odczytać, a nie po to, aby kompilator mógł utworzyć "szybszy" kod.

(Uwaga mój powyższy opis jest szczegółowo realizacja jak kompilator C# działa wewnętrznie: nie należy polegać na nim zawsze pracuje tak - w rzeczywistości, to może nawet nie działają dokładnie tak, jak tego teraz , ale przynajmniej jest to ogólny pomysł).

+0

+1: Dokładnie to, co zamierzałem powiedzieć. Są one zaimplementowane jako GOTO (sprawdź odbłyśnik w celu weryfikacji). To jest O (1) - naprawdę nie powinieneś się martwić o optymalizację w tym momencie ... – Khanzor

+2

Co z wszystkimi liczbami -1? –

+3

+1 ode mnie. Jeśli perf jest ** naprawdę ** problemem, rozważ użycie języka, który znajduje się bliżej metalu. –

-1

Myślę, że sposób, w jaki zmienia się obudowa przełącznika, spowoduje zapętlenie wszystkich skrzynek od góry do dołu, aby znaleźć dopasowanie. Jeśli pasuje, zatrzymuje się.

Tak więc, jeśli dokonano zmian do priorytetowego traktowania przypadków częstotliwości, odpowiedź jest twierdząca, to może pomóc w jakiś sposób z wydajnością. Ale wierzę, że to niewiele pomoże.

1

Mają taką samą wydajność dla relatywnie małego zestawu wartości. Próbowałem wcześniej sprawdzić kod zestawu programu C, kompilator tworzy tabelę skoku ze wszystkich wartości, które masz w swoim przełączniku.

Ale jeśli wartości są zbyt duże, to jest to bezpieczny zakład, że ulegną one degeneracji do ifelse if, więc umieszczenie na górze litery "E" z pewnością przyspieszy sprawę.

Jest również applicable w języku C#, C# również tworzy tabelę skoków dla instrukcji switch z małym zestawem, aczkolwiek tylko dla sąsiadujących wartości. Tak więc jest to O (1), nie ma wielokrotnego testowania, nawet jeśli pierwsze wartości nie pasują.

2

To zależy od sposobu, w jaki kompilator implementuje instrukcję switch.

Po pierwsze, nie można arbitralnie wstrzymać zamówienia; jeśli masz jeden blok sprawy w języku podobnym do języka C (C, C++, C#, Java, ...), a ten blok przypadku nie kończy się na , nie można zmienić kolejności spraw, ponieważ brak jest przerwa oznacza, że ​​kompilator musi wykonać przekierowanie do następnego przypadku. Jeśli zignorujemy tę szczególną sytuację, możesz przerobić pozostałe przypadki.

Jeśli liczba przypadków jest mała, kompilator może wykonać testy przypadków według sekwencji porównywanych. Jeśli liczba przypadków jest niewielka, może zbudować zrównoważone drzewo binarne z przypadków. Jeśli liczba przypadków jest duża, większość kompilatorów zaimplementuje indeksowaną gałąź na wartości przełącznika, jeśli jest to zbiór . Jeśli części zbioru wartości wielkości liter są gęste, a części nie są , kompilator może podzielić partycje na grupy przy użyciu drzewa binarnego , aby wybrać zestaw gęsty i skok indeksowany w zbiorze gęstym. (W rzeczywistości kompilator może technicznie zrobić wszystko, co przejdzie kontroli do odpowiedniego przypadku, ale najczęściej jest to jeden z powyższych).

Możesz zobaczyć, że zamówienie może mieć znaczenie lub nie, w zależności od tego, w jaki sposób kompilator implementuje przełącznik. Dla większości dobrych kompilatorów nie ma to większego znaczenia.

+1

Dla jasności, pytanie jest oznaczone C#, a C# nie obsługuje upadek poprzez przypadki, jak opisano w pierwszym akapicie tutaj (każdy przypadek musi być zakończone przez 'break' lub' return'). – Dusty

+0

Interesujące. Tak więc ludzie kodują "break" w C# niepotrzebnie (zobacz przykład OP). Tak naprawdę nie zmienia to mojej odpowiedzi. –

+0

Nie, C# jest trochę gadatliwy w tej sprawie, musisz jawnie zakończyć blokadę; 'break' (lub' return') jest wymagany (prawdopodobnie w celu zabezpieczenia się przed przypadkami upadku, które stają się przełomową zmianą, jeśli zostaną wprowadzone w przyszłości). Zgadzam się, twoja odpowiedź jest nadal poprawna i użyteczna. – Dusty