[Mono-list] callvirt instruction performance penalty?

Jonathan Pryor jonpryor at vt.edu
Tue Mar 12 02:22:57 UTC 2013

On Mar 11, 2013, at 8:47 PM, Nigel Delaney <nigel.delaney at outlook.com> wrote:
> However, I was surprised to learn when disassembling code that both Mono and Microsoft seem to ignore this optimization, and emit all method calls as callvirt instructions in IL regardless of whether or not they are actually virtual method calls, which seems to defeat the whole point of not being java.

No. That's a different optimization. ;-)

A callvirt to a non-virtual method does not bind the method virtually; it binds it non-virtually, and the method cannot be overridden.

So why use callvirt? Because callvirt implicitly checks the target's `this` reference and will throw a NullReferenceException when it's null.

It is thus a space optimization: instead of needing to null-check arguments everywhere, they just rely on callvirt to do the null check.

> A Microsoft employee blogged about this  (http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx)  and it seems that made this change to ensure that instance methods could not be called on null instance references (a debatable decision perhaps). 

I should finish reading emails before replying.

> I gather/suspect that Microsoft still optimizes the callvirt instruction during the JIT stage to call the method and check for a null reference,

This is not possible. At JIT time, they just have data, they just have instructions to execute. They don't have everything that won't be known until runtime. It's thus ~impossible (in the general case) to know if a given instance will be null at JIT time. You will know if the instance is null at runtime.

For example, consider string.Concat(). That method will be invoked from lots of different places in your code. It _cannot_ null check the arguments at JIT time, because there's only one (~random) code path that will hit it at JIT time, but it'll be invoke at runtime by lots of different places.

The null check thus must be at runtime.

> And if Mono also is able to optimize callvirt calls that are not actually virtual calls?

Yes. callvirts to non-virtual calls do not go through the virtual method table, they just directly call the target method after the null check.

 - Jon

More information about the Mono-list mailing list