How does compiler optimize virtual methods implemented by a sealed class

对着背影说爱祢 提交于 2019-12-11 06:48:01

问题


I'm wondering how the following code is optimized. Specifically concerning virtual and direct calls. I have commented on how I think everything is optimized but those are just guesses.

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

回答1:


The compiler doesn't do any type of optimization at all. It always generates an IL 'callvirt' instruction (call virtual). The runtime could in theory remove the virtual part of the call, but every benchmark I've seen and tried indicates this is not the case. Considering even fully static C++ compilers can't do this for trivial situations, it seems unlikely that the JIT is able to pull it off. And even if they could make it work, there's a vast range of much more common performance pitfalls they can spend their time on instead.

Oh, and this blog post by Eric Gunnerson explains why C# always generates callvirt.




回答2:


In the case where you have a virtual method on a sealed class, and the type of the object reference is the sealed class the virtual call could be avoided. Take the following example. There is no actual reason that GetName needs to be called virtually because we know there can be no sub class of Parent and hence no further virtual dispatch.

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

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

A compiler could choose to notice this and output a call IL instruction instead of a callvirt. However both the C# and VB.Net Compiler choose not to perform this optimization. Both will emit callvirt.

The JIT is also free to make such an optimization. It also chooses not to.

  • http://blogs.microsoft.co.il/blogs/sasha/archive/2007/02/27/JIT-Optimizations_2C00_-Inlining_2C00_-and-Interface-Method-Dispatching-_2800_Part-1-of-N_2900_.aspx

That does not mean however that you should not seal your classes. Classes should be sealed unless you actually intend for someone to inherit from them. Otherwise you're opening yourself up to scenarios you have failed to accurately cost.

Additionally, there's nothing stopping the compilers and JIT from implementing this at a later date.




回答3:


If it were sealed (maybe edit it?), the compiler or the JIT could issue a non-virtual call when the object is known to be a SealedChild, saving an indirection. Java does this, C# seems not to do it; I don't know what the JIT does though.



来源:https://stackoverflow.com/questions/770547/how-does-compiler-optimize-virtual-methods-implemented-by-a-sealed-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!