2015-11-24 10 views
7

Ich habe mich nur gefragt, ob eine einfache statische Funktion mit dem?: - Operator während der Just-in-Time-Compilierung inline ist. Hier ist ein beliebiges Beispiel, das den Code verwendet.Sind Methoden, die den?: Operator verwenden, während der JIT-Kompilierung inline?

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

, das die folgenden IL gibt:

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 

Oder die einfachere Alternative inlined werden würde?

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

Hier ist die IL für sie:

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 

Der Operator?: Scheinbar erzeugt einen prägnanten MSIL als die, wenn alternativen, aber weiß jemand, was während der JIT-Kompilierung geschieht? Sind beide inline? Sind beide inline?

+3

„Inlining“ ist ein Begriff, zu dem, was mit einem * Verfahren * Optimierer tut der Jitter gilt. Der?: -Operator ist keine Methode, der Maschinencode dafür wird immer "in der Zeile" generiert, auch im nicht optimierten Debug-Build. –

+1

Ihre Max-Methode? Sicher, es ist sehr klein und macht nichts icky, das der Optimierer nicht mag. Sie müssen den Titel Ihrer Frage korrigieren. –

+0

@Krythic Ich stimme Hans zu, du Titel ist ein bisschen irreführend. Es klingt so, als würden Sie fragen, ob der '?:' - Operator selbst inline ist und nicht die Methoden, die '?:' Enthalten, inline sind, was Ihre eigentliche Frage ist. –

Antwort

6

Wie können wir herausfinden? Werfen wir nur einen Blick auf den generierten Code.

Hier ist ein Testprogramm:

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); 
    } 
} 

Zuerst lassen Sie uns einige Dinge klarstellen. In einer Veröffentlichung bauen, die IL von MaxA ist (auf 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 

Für MaxB, es ist:

.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 

So ist die IL ist symmetrisch für beide Funktionen (es ist der gleiche Code, außer dass die Reihenfolge der Zweige sowie der Verzweigungsbefehl invertiert sind).

Jetzt überprüfen wir, wie der x64-Code von TestA und TestB aussieht.

TestA, x64, 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 

Sie können sehen, dass die MaxA Funktion inlined ist (es gibt keine call Anweisung und Sie können die jg"springen, wenn mehr" Verzweigungsanweisung deutlich zu sehen).

TestB, x64:

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

Es überrascht nicht, bekommen wir das gleiche Ergebnis.

Für compelteness, hier MaxA auf 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 auch. (Debug -> Fenster -> Demontage)


Als Referenz können Sie den erzeugten Assembler-Code mit dem Zerlegen Fenster überprüfen, wenn Sie auf einem Breakpoint sind, aber zuerst sicher, dass Sie die Suppress JIT deaktivieren machen Optimierung auf Modullast Option:

option to uncheck

3

Eine Methode mit ternärem Operator kann inline sein. Eine Methode mit if/else auch. Natürlich hängt alles von den anderen Operationen in der Methode ab.

Eine einfache Möglichkeit, dies zu überprüfen, besteht darin, eine Ausnahme in die Methode zu werfen und die Stapelverfolgung zu überprüfen. Wenn die Methode inline war, wird sie nicht im Stack-Trace angezeigt.

Der folgende Code

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; 
    } 
} 

in .NET 4.6 64-Bit (Release-Build) die folgende Ausnahme auslöst:

System.DivideByZeroException: Versuchten durch null zu dividieren.

bei Test.Program.Main (String [] args)

Keine ThrowInTernaryOperator im Stack-Trace, so wurde es inlined.

Das Verhalten kann in verschiedenen Versionen von .NET und 32/64-Bit-Architekturen unterschiedlich sein.