2012-04-01 6 views
6

私は主にC#を使って開発していますが、この質問は他の言語にも適していると思います。
また、ここにはたくさんのコードがあるようですが、質問は非常に簡単です。
絶対リターンパスを持つメソッドはどのようにインライン化されますか?

私が理解するように、インライン展開は、メソッドが呼び出されたすべての場所にメソッドの本体を挿入することによって、メソッド呼び出しを置き換えるコンパイラ(C#仮想マシンの場合)です。

のは、私は次のプログラムがあるとしましょう:私はコードがメソッドをインライン展開した後、次のようになりますどのように理解することができ

bool IsEven(int n) 
{ 
    if (n % 2 == 0) // Two conditional return paths 
     return true; 
    else 
     return false; 
} 

:メソッドIsEvenのボディ...

static Main() 
{   
    int number = 7; 
    bool a; 
    a = IsEven(number); 
    Console.WriteLine(a); 
} 

static Main() 
{ 
    int number = 7; 
    bool a; 
    if (number % 2 == 0) 
     a = true; 
    else 
     a = false; 
    Console.WriteLine(a); // Will print true if 'number' is even, otherwise false 
} 

明らかに単純で正しいプログラムです。

しかし、私は絶対リターンパスを含めるようにIsEvenの身体を少し微調整であれば...

bool IsEven(int n) 
{ 
    if (n % 2 == 0) 
     return true; 
    return false; // <- Absolute return path! 
} 

私個人的には、いくつかの状況で、もう少しこのスタイルのように。一部の屈折工具は、私がこのように見えるように最初のバージョンを変更することを示唆するかもしれませんが、インラインでどのように見えるか想像してみると、私は困惑しました。

static Main() 
{ 
    int number = 7; 
    bool a; 
    if (number % 2 == 0) 
     a = true; 
    a = false; 
    Console.WriteLine(a); // Will always print false! 
} 

質問頼まれる:
我々は方法の第二のバージョンをインライン化した場合

はどのように絶対リターンパスを持つメソッドをインライン化するとコンパイラ/仮想マシンの契約?
このようなことでメソッドのインライン展開が実際に妨げられることはほとんどないようです。そのようなことがどう対処されているのだろうかと思います。おそらく、インライン展開のプロセスはこれほど簡単ではありませんか?おそらく1つのバージョンがVMによってインライン化される可能性が高いでしょうか?

編集: は両方の方法(および最初のものを手動でインライン化)をプロファイリング少なくとも私に(同じまたは類似の方法で性能に差を示さなかったので、私は唯一の両方の方法は、インライン化を受けることを想定することができ、作業VM)。
また、これらのメソッドは非常に単純でほとんど互換性があるようですが、絶対リターンパスを持つ複雑なメソッドは絶対リターンパスを持たないバージョンに変更するのがはるかに難しいかもしれません。

答えて

6

JITterがインラインで行うことを説明するのは難しいことです。インライン化を行うためにC#コードを変更することはありません。生成されたバイト(コンパイル済みのバージョン)アセンブリコード(実際のマシンコードバイト)を生成するときに持っている「ツール」は、C#(またはその点についてはIL)よりもはるかに細分化されています。

つまり、breakキーワードを考えれば、C#用語ではどのように動作するかを知ることができます。

は些細ではありませんすべてのインライン関数がwhile (true)ループ(またはdo while(false)ループ)で囲まれている可能性について考えてみましょう - と、すべてのソースリターンが文のlocalVar = result; break;セットに変換されています。そして、あなたはこのような何かを得る:アセンブリを製造する際

static Main() 
{ 
    int number = 7; 
    bool a; 
    while (true) 
    { 
     if (number % 2 == 0) 
     { 
      a = true; 
      break; 
     } 
     a = false; 
     break; 
    } 

    Console.WriteLine(a); // Will always print the right thing! Yey! 
} 

を同様に、あなたは、生成されたjmp秒のLOTが表示されます - これらは、break文の道徳的等価ですが、彼らははるかに柔軟です(考えます彼らは匿名のものや何かのものとして)。

ジッタ(およびネイティブにコンパイルされたコンパイラ)には、「正しいこと」を実行するために使用できる多くのツールが用意されています。

+1

btw:このwhile(true)breakのメカニズムは、他のものにも便利です。私は頻繁には使用しませんが、必要に応じて実行する必要がある一連の操作いつでも停止しておくと便利です。同様に、ネイティブ言語では、「最終的に」コンストラクタを持っていないときにそれをシミュレートするために使用されます(私はそれをいくつかのプロジェクトでかなり使いました) –

3

return文は示しています。結果値

  • のC#では、ローカル変数の
  • 破壊 (これはC++ではなく、C#のに適用される)、finallyブロックとusingブロック中のコールを廃棄して実行されます。
  • 機能のうち
  • ジャンプ

これらすべてのものはまだインライン化の後に起こります。ジャンプはインライン展開後にクロス関数ではなくローカルになりますが、まだそこに存在します。

インライン展開は、C/C++マクロのようなテキスト置換ではありません。

インライン化とテキスト置換の違いは、同じ名前の変数の処理です。

2

cpuが実行するマシンコードは非常に単純な言語です。それはreturnステートメントの概念を持たず、サブルーチンには単一の入口点と単一の出口点があります。したがって、このようなあなたのISEVEN()メソッドは:

bool IsEven(int n) 
{ 
    if (n % 2 == 0) 
     return true; 
    return false; 
} 

この(ない有効なC#)を似たものに、ジッタによって書き換えする必要があります

void IsEvent(int n) 
{ 
    if (n % 2 == 0) { 
     $retval = true; 
     goto exit; 
    } 
    $retval = false; 
exit: 
} // $retval becomes the function return value 

$ RETVAL変数は、ここで偽物に見えるかもしれません。それはx86コア上のEAXレジスタです。このコードはインライン化が簡単で、Main()本体に直接移植することができます。 $ retval変数は、単純な論理置換を使用して変数aと同じにすることができます。

関連する問題