How does the callvirt .NET instruction work for interfaces?

前端 未结 2 2010
半阙折子戏
半阙折子戏 2021-01-31 08:29

Explaining virtual dispatching to someone is easy: every object has a pointer to a table as part of its data. There are N virtual methods on the class. Every call to a particula

相关标签:
2条回答
  • 2021-01-31 09:00

    Because the compiler always has to have an actual object on which to call the method (at runtime), it always knows at runtime the concrete type that it is dealing with.

    The code that calls a virtual method firstly determines the type of the object being used. It then consults the type's method table to lookup the method being called. The code then simply calls that method, passing the object's reference as 'this' along with any other parameters.

    I suspect the crucial bit you're interested in is how the code looks up the address of the method in the type's method table.

    More details about the method table are given in the "JIT and Run" article in the May 2005 edition of MSDN magazine (which at the time of writing can be downloaded as a ".chm" from this page - but you will have to go to the file's properties to unlock it before it will display properly, due to security restrictions.)

    It's still a bit hand-wavy about exactly how the lookup is done, but it does give quite a lot of other details.

    0 讨论(0)
  • 2021-01-31 09:03

    Interface dispatching in the CLR is black magic.

    As you correctly note, virtual method dispatch is conceptually easy to explain. And in fact I do so in this series of articles, where I describe how you could implement virtual methods in a C#-like language that lacked them:

    http://blogs.msdn.com/b/ericlippert/archive/2011/03/17/implementing-the-virtual-method-pattern-in-c-part-one.aspx

    The mechanisms I describe are quite similar to the mechanisms actually used.

    Interface dispatch is much harder to describe, and the way the CLR implements it is not at all apparent. The CLR mechanisms for interface dispatch have been carefully tuned to provide high performance for the most common situations, and the details of those mechanisms are therefore subject to change based as the CLR team develops more knowledge about real-world patterns of usage.

    Basically the way it works behind the scenes is that each call site -- that is, each point in the code where an interface method is invoked -- there is a little cache that says "I think the method associated with this interface slot is... here". The vast majority of the time, that cache is right; you very seldom call the same interface method a million times with a million different implementations. It's usually the same implementation over and over again, many times in a row.

    If the cache turns out to be a miss then it falls back to a hash table that is maintained, to do a slightly slower lookup.

    If that turns out to be a miss, then the object metadata is analyzed to determine what method corresponds to the interface slot.

    The net effect is that at a given call site, if you always invoke an interface method that maps to a particular class method, it is very fast. If you always invoke one of a handful of class methods for a given interface method, performance is pretty good. The worst thing to do is to never invoke the same class method twice with the same interface method at the same site; that takes the slowest path every time.

    If you want to know how the tables for the slow lookup are maintained in memory, see the link in Matthew Watson's answer.

    0 讨论(0)
提交回复
热议问题