2015-11-24 65 views
7

Zastanawiam się, czy prosta funkcja statyczna wykorzystująca operator?: Jest wbudowana podczas kompilacji na czas. Oto dowolny przykład użycia kodu.Czy metody używają instrukcji?: Operator Inlined podczas kompilacji JIT?

public static int Max(int value, int max) 
    { 
     return value > max ? max : value; 
    } 

co daje następujące IL:

Max: 
IL_0000: nop   
IL_0001: ldarg.0  
IL_0002: ldarg.1  
IL_0003: bgt.s  IL_0008 
IL_0005: ldarg.0  
IL_0006: br.s  IL_0009 
IL_0008: ldarg.1  
IL_0009: stloc.0  
IL_000A: br.s  IL_000C 
IL_000C: ldloc.0  
IL_000D: ret 

A może być prostsza alternatywa inlined?

 public static int Max(int value, int max) 
     { 
      if (value > max) 
      { 
       return max; 
      } 
      else 
      { 
       return value; 
      } 
     } 

Oto IL dla niego:

Max: 
IL_0000: nop   
IL_0001: ldarg.0  
IL_0002: ldarg.1  
IL_0003: cgt   
IL_0005: stloc.0  
IL_0006: ldloc.0  
IL_0007: brfalse.s IL_000E 
IL_0009: nop   
IL_000A: ldarg.1  
IL_000B: stloc.1  
IL_000C: br.s  IL_0013 
IL_000E: nop   
IL_000F: ldarg.0  
IL_0010: stloc.1  
IL_0011: br.s  IL_0013 
IL_0013: ldloc.1  
IL_0014: ret 

: operator widocznie generuje bardziej zwięzły MSIL niż gdyby alternatywa, ale czy ktoś wie co się dzieje podczas kompilacji JIT? Czy obaj są zainspirowani? Czy któryś z nich jest włączony?

+3

„inline” to termin, który odnosi się do tego, co robi z optymalizator jitter metody * *. Operator?: Nie jest metodą, kod maszynowy zawsze jest generowany "w linii", nawet w niezoptymalizowanej wersji debugowania. –

+1

Twoja metoda Max? Oczywiście, jest bardzo mały i nie robi nic złego, że optymalizator nie lubi. Musisz poprawić swój tytuł pytania. –

+0

@Krythic Zgadzam się z Hansem, ty tytuł jest trochę mylący. Brzmi to tak, jakbyś pytał, czy sam operator '?:' Jest zainicjowany, a nie metody zawierające '?:' Są wstawione, co jest twoim prawdziwym pytaniem. –

Odpowiedz

6

Jak możemy się dowiedzieć? Rzućmy okiem na wygenerowany kod.

Oto program testowy:

internal static class Program 
{ 
    public static int MaxA(int value, int max) 
    { 
     return value > max ? max : value; 
    } 

    public static int MaxB(int value, int max) 
    { 
     if (value > max) 
      return max; 
     else 
      return value; 
    } 

    [MethodImpl(MethodImplOptions.NoInlining)] 
    private static int TestA(int a, int b) 
    { 
     return MaxA(a, b); 
    } 

    [MethodImpl(MethodImplOptions.NoInlining)] 
    private static int TestB(int a, int b) 
    { 
     return MaxB(a, b); 
    } 

    private static void Main() 
    { 
     var rand = new Random(); 
     var a = rand.Next(); 
     var b = rand.Next(); 

     var result = TestA(a, b); 
     Console.WriteLine(result); 

     result = TestB(a, b); 
     Console.WriteLine(result); 
    } 
} 

Najpierw przejdźmy kilka rzeczy prosto. W wydania budowy, IL z MaxA jest (na Roslyn):

.method public hidebysig static 
    int32 MaxA (
     int32 'value', 
     int32 max 
    ) cil managed 
{ 
    // Method begins at RVA 0x2050 
    // Code size 8 (0x8) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldarg.1 
    IL_0002: bgt.s IL_0006 

    IL_0004: ldarg.0 
    IL_0005: ret 

    IL_0006: ldarg.1 
    IL_0007: ret 
} // end of method Program::MaxA 

Dla MaxB, to:

.method public hidebysig static 
    int32 MaxB (
     int32 'value', 
     int32 max 
    ) cil managed 
{ 
    // Method begins at RVA 0x2059 
    // Code size 8 (0x8) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldarg.1 
    IL_0002: ble.s IL_0006 

    IL_0004: ldarg.1 
    IL_0005: ret 

    IL_0006: ldarg.0 
    IL_0007: ret 
} // end of method Program::MaxB 

Więc IL jest symetryczny dla obu funkcji (jest to ten sam kod, z wyjątkiem odwrócenia kolejności oddziałów oraz instrukcji rozgałęzień).

Teraz sprawdźmy, jak wygląda kod x64 TestA i TestB.

TestA, 64, RyuJIT:

  return MaxA(a, b); 
00007FFED5F94530 cmp   ecx,edx 
00007FFED5F94532 jg   00007FFED5F94538 
00007FFED5F94534 mov   eax,ecx 
00007FFED5F94536 jmp   00007FFED5F9453A 
00007FFED5F94538 mov   eax,edx 
00007FFED5F9453A ret 

Można zobaczyć, że funkcja MaxA jest inlined (nie ma instrukcji call i można wyraźnie zobaczyć jg"skok Jeżeli wartość" instrukcji rozgałęzień).

TestB, 64:

  return MaxB(a, b); 
00007FFED5F94550 cmp   ecx,edx 
00007FFED5F94552 jle   00007FFED5F94558 
00007FFED5F94554 mov   eax,edx 
00007FFED5F94556 jmp   00007FFED5F9455A 
00007FFED5F94558 mov   eax,ecx 
00007FFED5F9455A ret 

zaskoczeniem, możemy uzyskać ten sam rezultat.

Dla compelteness, oto MaxA na x86:

  return MaxA(a, b); 
00A32E22 in   al,dx 
00A32E23 cmp   ecx,edx 
00A32E25 jg   00A32E2B 
00A32E27 mov   eax,ecx 
00A32E29 jmp   00A32E2D 
00A32E2B mov   eax,edx 
00A32E2D pop   ebp 
00A32E2E ret 

inlined również.


Dla porównania, można sprawdzić wygenerowany kod montaż przy demontażu oknie (Debug -> Windows -> Demontaż) gdy jesteś na przerwania, ale najpierw upewnij się, odznacz zwalczaniu JIT optymalizacja na opcją ładowania modułu:

option to uncheck

3

Metoda z potrójnym operatorem może być inline. Metoda z if/else. Oczywiście wszystko zależy od innych operacji w metodzie.

Łatwym sposobem sprawdzenia jest zgłoszenie wyjątku w metodzie i sprawdzenie śladu stosu. Jeśli metoda została wstawiona, nie pojawi się w śladzie stosu.

Następujący kod

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      int i = ThrowInTernaryOperator(1, 0); 
     } 
     catch (DivideByZeroException ex) 
     { 
      Console.WriteLine(ex.ToString()); 
     } 
    } 

    public static int ThrowInTernaryOperator(int value, int max) 
    { 
     return value > max ? value/0 : 0; 
    } 
} 

w NET 4,6 64-bitowe (budowy uwalnianiu) wyrzuca następujące wyjątki:

System.DivideByZeroException: Próba podziału przez zero.

w Test.Program.Main (string [] arg)

nr ThrowInTernaryOperator w ślad stosu, tak, że jest wplatany.

Urządzenie może różnić się w różnych wersjach architektur .NET i 32/64 bit.

Powiązane problemy