2009-04-20 10 views
1

次のコードがどのように最適化されているのだろうか。具体的には、仮想通話と直接通話に関するものです。私はすべてが最適化されていると思う方法についてコメントしましたが、それはちょうど推測です。コンパイラは密接なクラスによって実装された仮想メソッドを最適化する方法

public abstract class Super 
{ 
    public abstract void Foo(); 

    public void FooUser() 
    { 
     Foo(); 
    } 
} 

public class Child1 : Super 
{ 
    public override void Foo() 
    { 
     //doSomething 
    } 
} 

public class SealedChild : Super 
{ 
    public override void Foo() 
    { 
     //doSomething 
    } 
} 

class Program 
{ 
    void main() 
    { 
     Child1 child1 = new Child1(); 
     child1.Foo(); //Virtual call? 
     child1.FooUser(); //Direct call and then a virtual call. 

     SealedChild sealedChild = new SealedChild(); 
     sealedChild.Foo(); //Direct call? 
     sealedChild.FooUser(); 
     /* Two options: either a direct call & then a virtual call 
     * Or: direct call with a parameter that has a function pointer to Foo, and then a direct call to foo. 
     */ 

     Super super = child1; 
     super.Foo(); //Virtual call. 
     super.FooUser(); //Virtual call then direct call. 
    } 
} 
+0

"SealedClass"は実際には封印されていません。だからあなたのChild1クラスと変わりはありません。 –

+0

C#?それは知るのを助けるだろう。 –

答えて

5

コンパイラは最適化のタイプをまったく行いません。これは常にILの 'callvirt'命令(仮想呼び出し)を生成します。ランタイムは、理論的にはコールの仮想部分を削除することができますが、私が見て試したベンチマークは、そうではないことを示しています。まったく静的なC++コンパイラでさえ、簡単な状況ではこれを行うことはできないと考えると、JITがそれを取り除くことは難しいようです。たとえそれを機能させることができたとしても、代わりに時間を費やすことができる非常に多くの一般的なパフォーマンス落とし穴があります。

ああ、this blog post by Eric Gunnersonは、なぜC#が常にcallvirtを生成するのかを説明しています。

0

コンパイルまたはJIT は、オブジェクトがSealedChildであるとわかっているときに非仮想呼び出しを発行し、間接参照を保存できます。 Javaはこれを行い、C#はそれをしないようです。私はJITが何をしているのか分かりません。

5

封印されたクラスに仮想メソッドがあり、オブジェクト参照のタイプが封印されたクラスである場合は、仮想呼び出しを避けることができます。次の例を考えてみましょう。 Parentのサブクラスがなく、それ以上の仮想ディスパッチがないことがわかっているので、GetNameを仮想的に呼び出す必要がある実際の理由はありません。

sealed class Parent : Child { 
    public override string GetName() { return "foo"; } 
} 

public void Test() { 
    var p = new Parent(); 
    var name = p.GetName(); 
} 

コンパイラはこれに気づき、callvirtではなくcall IL命令を出力することができます。ただし、C#とVB.Netの両方のコンパイラでは、この最適化を実行しません。両方ともコールヴィルトを放出する。

JITは、このような最適化を行うことも自由です。それはまた選ぶこともしない。あなたのクラスをシールしてはならないことが意味するものではありません

。あなたが実際にそれらから継承するつもりがない限り、クラスは封印されるべきです。さもなければ、あなたは正確にコストを払わなかったシナリオまで自分自身を開いています。

さらに、コンパイラとJITが後でこの実装を停止することは何もありません。

関連する問題