2012-02-27 4 views
7

Tworzyłem metodę C# w visual studio, która zawierała tylko instrukcję switch, w której każdy przypadek zwrócił wartość. Przez osobiste przyzwyczajenia, kładę coś podobnego do następującego kodu:C# Switch Statement: Bardziej wydajne, aby nie używać domyślnego?

private string SwitchMethod(int num) 
    { 
     switch (num) 
     { 
      case 0: 
       return "result 1"; 
      case 1: 
       return "result 2"; 
      case 2: 
       return "result 3"; 
     } 
     return "no result"; 
    } 

Moje pytanie brzmi: Który kod będzie mieć lepszą wydajność? Kod powyżej lub poniżej, czy są takie same? I dlaczego?

Założę, że z powodu optymalizacji kompilacji ... mogą być po prostu takie same ... ale tak naprawdę nie wiem.

private string SwitchMethod(int num) 
    { 
     switch (num) 
     { 
      case 0: 
       return "result 1"; 
      case 1: 
       return "result 2"; 
      case 2: 
       return "result 3"; 
      default: 
       return "no result"; 
     } 
    } 

ZMIAN: Wydaje się, że powinienem być bardziej szczegółowe: Kiedy skompilowany ... będzie mniej wydajny kod generowany przez jednego lub drugiego?

Zdaję sobie sprawę, że różnica w wydajności może być niewielka ... Jestem po prostu ciekawa.

+4

Jest to dość subiektywne. Unikam wielu punktów wyjścia z metody lub funkcji, ale YMMV. Jeśli jesteś naprawdę ciekawy, spójrz na IL. –

+1

To skompilowane? Nie stoję przed moim kompilatorem, ale jestem prawie pewien, że wymagane jest ustawienie domyślne. –

+0

Wyrzuć wyjątek, mówiąc "nie powinno się tu dostać" jako domyślny, jeśli naprawdę myślisz, że to się nie stanie – Matt

Odpowiedz

9
public static string foo(int num) 
     { 
      switch (num) 
      { 
       case 0: 
        return "result 1"; 
       case 1: 
        return "result 2"; 
       case 2: 
        return "result 3"; 
      } 
      return "no result"; 
     } 

Staje:

.method public hidebysig static string foo(int32 num) cil managed 
{ 
    // Code size  57 (0x39) 
    .maxstack 1 
    .locals init ([0] string CS$1$0000, 
      [1] int32 CS$4$0001) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: stloc.1 
    IL_0003: ldloc.1 
    IL_0004: switch  ( 
         IL_0017, 
         IL_001f, 
         IL_0027) 
    IL_0015: br.s  IL_002f 
    IL_0017: ldstr  "result 1" 
    IL_001c: stloc.0 
    IL_001d: br.s  IL_0037 
    IL_001f: ldstr  "result 2" 
    IL_0024: stloc.0 
    IL_0025: br.s  IL_0037 
    IL_0027: ldstr  "result 3" 
    IL_002c: stloc.0 
    IL_002d: br.s  IL_0037 
    IL_002f: ldstr  "no result" 
    IL_0034: stloc.0 
    IL_0035: br.s  IL_0037 
    IL_0037: ldloc.0 
    IL_0038: ret 
} // end of method Program::foo 

Moving powrót do domyślnego przypadku:

.method public hidebysig static string foo(int32 num) cil managed 
{ 
    // Code size  57 (0x39) 
    .maxstack 1 
    .locals init ([0] string CS$1$0000, 
      [1] int32 CS$4$0001) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: stloc.1 
    IL_0003: ldloc.1 
    IL_0004: switch  ( 
         IL_0017, 
         IL_001f, 
         IL_0027) 
    IL_0015: br.s  IL_002f 
    IL_0017: ldstr  "result 1" 
    IL_001c: stloc.0 
    IL_001d: br.s  IL_0037 
    IL_001f: ldstr  "result 2" 
    IL_0024: stloc.0 
    IL_0025: br.s  IL_0037 
    IL_0027: ldstr  "result 3" 
    IL_002c: stloc.0 
    IL_002d: br.s  IL_0037 
    IL_002f: ldstr  "result 4" 
    IL_0034: stloc.0 
    IL_0035: br.s  IL_0037 
    IL_0037: ldloc.0 
    IL_0038: ret 
} // end of method Program::foo 

dokładnie takie same. Brak różnicy w wydajności. Zmieniłem "brak wyniku" na wynik 4, aby upewnić się, że kod został zregenerowany. Podobno kompilator C# go optymalizuje lub po prostu staje się równoważny.

+0

Dziękuję bardzo! Wybacz mojej niewiedzy ... ale czego użyłeś do wygenerowania zgromadzenia? – bsara

+2

@Brandon Jest ich mnóstwo. Na przykład ilspy –

+2

* ILDasm * jest dostarczany z .NET Framework SDK. Należy pamiętać, że jest to kod zespołu IL, który nie jest tym, co widzi procesor. Możesz użyć narzędzia, które jest dostarczane wraz z narzędziem SDK NGen.exe, aby wygenerować ten zestaw, ale prawdopodobnie zoptymalizuje go w różny sposób w zależności od sprzętu. –

4

Dobrą praktyką jest zawsze uwzględniać domyślny przypadek u podstawy przełącznika w sytuacjach, w których żaden z pozostałych przypadków nie jest ważny.

Nie jest to problem związany z wydajnością, ponieważ jeśli jeden z wcześniejszych przypadków został trafiony - Twój program nie sprawdzi żadnych innych przypadków (jest to odpowiednik użycia, jeśli/else if/else - gdzie ostateczna wartość jest domyślna) .

Mam nadzieję, że to pomoże.

+0

Twoja odpowiedź jest w porządku, ale należy zachować ostrożność przy przyjmowaniu tego, co masz na myśli, równoważąc się: http://stackoverflow.com/questions/445067/if-vs-switch-speed –

+0

Ale kiedy skompilowany ... czy więcej kodu będzie generowane przez jeden lub drugi? W ten sposób powstaje więcej instrukcji wykonywanych, gdy żaden z oczekiwanych warunków nie jest spełniony? – bsara

+0

Więcej kodu nie zawsze oznacza mniej wydajne. –

3

Zamiast funkcji i instrukcji przełącznika, lepiej byłoby użyć ogólnego słownika, w którym kluczem jest KofaxEnvironment, a wartość jest taka sama, jak przy powrocie z przełącznika. Coś jak:

Dictionary<KofaxEnvironment, string> 

lub

Dictionary<int, string> 

ja także nie martwić się o wydajność. Poprawność powinna być twoim pierwszym celem.

Ale, jeśli nie trzymać się z przełącznika użyć domyślnego że zgłasza wyjątek:

default: 
    throw new ArgumentException("Serious programmer error!"); 

A jeśli chodzi o wydajność, różnica (jeśli w ogóle) pomiędzy domyślnie switch i spada aż do powrót będzie znikomy.

+0

To nie jest dokładnie to, do czego dążyłem ... ale to świetny pomysł ... na pewno skorzystam z niego w przyszłości. Znacznie czystszy kod! – bsara