2015-11-24 6 views
7

ちょうどコンパイル時に?:演算子を使った単純な静的関数がインライン展開されているのかどうか疑問に思っていました。コードを使用した任意の例を示します。?:演算子を使用するメソッドはJITコンパイル時にインライン展開されますか?

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 

またはsimplierの代替がインライン展開されるだろう:以下ILを与える

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

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 

:?ここ

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

はそれのためのILあるオペレータが明らかであれば、代替よりも、より簡潔なMSILを生成しますが、誰もがJITコンパイル時に何が起こるか知っているのですか?両方ともインライン化されていますか?どちらかがインライン化されていますか?

+3

「インライン化は」ジッタオプティマイザは* *メソッドで何をするかにも適用される用語です。 ?:演算子はメソッドではなく、最適化されていないDebugビルドの場合でも、そのマシンコードは常に "インライン"で生成されます。 –

+1

あなたの最大の方法は?確かに、それは非常に小さく、オプティマイザが気に入らないものは何もしません。質問のタイトルを修正する必要があります。 –

+0

@Krythic私はHansに同意します、あなたはタイトルが少し誤解を招いています。 '?:'を含むメソッドがインライン展開されるのではなく、 '?:'演算子自体がインライン展開されているかどうかを尋ねるようなものです。 –

答えて

6

どうすればわかりますか?生成されたコードを見てみましょう。

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

まずは、いくつかのものがまっすぐ取得してみましょう:

は、ここでのテストプログラムです。 リリースビルドでは、MaxAのILは(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 

MaxBために、それはです:

.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 

だから、ILは、両方の機能(それは同じコードだために対称です、分岐命令と分岐命令の順序は逆になります)。

ここで、TestATestBのx64コードがどのように見えるかを確認しましょう。

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 

あなたはMaxA関数がインライン化されていることがわかりますが(何call指示がないと、あなたはjg"ジャンプ大きい場合は"分岐命令をはっきり見ることができます)。

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 

当然のことながら、我々は同じ結果を得ます。

compeltenessについては、こちらのx86上のMaxAです:

  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 

インラインにも。参考のため


、あなたは分解ウィンドウで生成されたアセンブリコードを確認することができますが、ブレークポイントにしているとき(デバッグ - >解体 - > Windowsの場合)が、最初は必ず抑制するJITをオフにしますモジュールのロードオプションの最適化:

option to uncheck

3

三項演算子を使用する方法をインライン化することができます。 if/elseの方法もあります。もちろん、メソッド内の他の操作に依存します。

簡単な方法は、メソッドに例外をスローし、スタックトレースをチェックすることです。メソッドがインライン化されていれば、スタックトレースには表示されません。

.NET 4.6 64ビット(リリースビルド)で

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

は、以下の例外をスロー次のコード

System.DivideByZeroExceptionはゼロで除算しようとしました。 Test.Program.Main(文字列[]引数)スタックトレースで

ThrowInTernaryOperator

なしで

、それをインライン化しました。

異なるバージョンの.NETおよび32/64ビットアーキテクチャでは、動作が異なる場合があります。

関連する問題