2009-04-20 8 views
1

Je me demande comment le code suivant est optimisé. Spécifiquement concernant les appels virtuels et directs. J'ai commenté comment je pense que tout est optimisé mais ce ne sont que des suppositions.Comment le compilateur optimise-t-il les méthodes virtuelles implémentées par une classe scellée?

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

Votre "SealedClass" n'est pas définie comme scellée. Ce ne serait donc pas différent de votre classe Child1. –

+0

C#? Ça aiderait à savoir. –

Répondre

5

Le compilateur n'effectue aucun type d'optimisation. Il génère toujours une instruction IL 'callvirt' (appel virtuel). Le moteur d'exécution pourrait en théorie supprimer la partie virtuelle de l'appel, mais chaque repère que j'ai vu et essayé indique que ce n'est pas le cas. Considérant que même les compilateurs C++ complètement statiques ne peuvent pas le faire pour des situations triviales, il semble peu probable que le JIT soit capable de le retirer. Et même s'ils peuvent le faire fonctionner, il existe une vaste gamme d'écueils de performance beaucoup plus communs sur lesquels ils peuvent passer leur temps. Oh, et this blog post by Eric Gunnerson explique pourquoi C# génère toujours callvirt.

0

S'il était scellé (peut-être l'éditer?), Le compilateur ou le JIT pouvait émettre un appel non virtuel lorsque l'objet est connu comme SealedChild, en sauvegardant une indirection. Java fait cela, C# ne semble pas le faire; Je ne sais pas ce que fait le JIT.

5

Dans le cas où vous avez une méthode virtuelle sur une classe scellée, et le type de la référence de l'objet est la classe scellée l'appel virtuel pourrait être évité. Prenons l'exemple suivant. Il n'y a aucune raison réelle que GetName ait besoin d'être appelé virtuellement parce que nous savons qu'il ne peut y avoir aucune sous-classe de Parent et donc aucune autre distribution virtuelle. Un compilateur peut choisir de remarquer ceci et de sortir une instruction d'appel IL au lieu d'un callvirt. Cependant, le compilateur C# et VB.Net choisissent de ne pas effectuer cette optimisation. Les deux vont émettre callvirt.

Le JIT est également libre de faire une telle optimisation. Il choisit également de ne pas le faire.

Cela ne signifie pas pour autant que vous ne devriez pas sceller vos classes. Les cours devraient être scellés à moins que vous n'ayez l'intention d'en hériter. Sinon, vous vous ouvrez à des scénarios que vous n'avez pas réussi à calculer avec précision.

De plus, rien n'empêche les compilateurs et JIT de l'implémenter à une date ultérieure.

Questions connexes