how slow is callvirt vs. call?

  • Thread starter Thread starter ThomasR
  • Start date Start date
T

ThomasR

I understand that virtual methods on inherited objects are slower than
non-virtual methods because of the indirection required to support the
call.

However, when looking at IL code produced by the compiler, I notice
that all methods on object variables use callvirt instead of call,
regardless of whether the method is truly virtual or not.

I assume this allows .NET to check if the object reference is null or
not before invoking the method. However, this seems very wasteful
when many methods are called on 1 object reference.

So, my questions are:
1) Can anyone quantify how much slower callvirt is than call?
2) Why couldn't the compiler check object reference for null on first
call, then replace all subsequent calls (when it knows the object ref
coudln't change) with IL call instead of IL callvirt?
3) If the overhead is at all significant, is there anyway to make the
compiler more efficient?
 
Hi ThomasR,
See my opinion inlined.
However, when looking at IL code produced by the compiler, I notice
that all methods on object variables use callvirt instead of call,
regardless of whether the method is truly virtual or not.

Both call and callvirt can be used to call instance virtaul or non-virtual
methods. There are some diferences between them, though.
So, my questions are:
1) Can anyone quantify how much slower callvirt is than call?

Don't warry about performance. First of all both virtual and non-virtual
methods are called indirectly the techique to find the right overriden
method to call is different than the one used by c++ and the performance hit
is not as significant.
Second of all don't forget that this is not the last compilation. There is
JIT compiler which compiles the IL code. even though callvirt is used JIT
compiler will generate code according to the target method.
2) Why couldn't the compiler check object reference for null on first
call, then replace all subsequent calls (when it knows the object ref
coudln't change) with IL call instead of IL callvirt?

I believe that the actual code at execution time won't check for null
reference each time a method is about to be called. Most of the safety
checkings JITter does only once when compiles the IL code.
3) If the overhead is at all significant, is there anyway to make the
compiler more efficient?

I believe using callvirt insead of call for non-virtual methods are just
hint to the JIT compiler that the address used to reference the object can
be null. So the JITter can include some code for checking for null or
incorrect addresses, but how I said it will do that in optimized fashion
and won't do this check twice if it is not necessary.
C# compiler emits *call* instruction whenever structure method is called.
Because of the nature of value types the address used to reference the value
type cannot be null.

HTH
B\rgds
100
 
(e-mail address removed) (ThomasR) wrote in
I understand that virtual methods on inherited objects are
slower than non-virtual methods because of the indirection
required to support the call.

However, when looking at IL code produced by the compiler, I
notice that all methods on object variables use callvirt instead
of call, regardless of whether the method is truly virtual or
not.

I assume this allows .NET to check if the object reference is
null or not before invoking the method. However, this seems
very wasteful when many methods are called on 1 object
reference.

So, my questions are:
1) Can anyone quantify how much slower callvirt is than call?
2) Why couldn't the compiler check object reference for null on
first call, then replace all subsequent calls (when it knows the
object ref coudln't change) with IL call instead of IL callvirt?
3) If the overhead is at all significant, is there anyway to
make the compiler more efficient?

Thomas,

IL produced by the C# compiler is generally unoptimized.

The IL is compiled by the JITter, which will probably apply
optimizations to turn any "convertable" callvirts into calls.


Chris.
 
ThomasR said:
I understand that virtual methods on inherited objects are slower than
non-virtual methods because of the indirection required to support the
call.

However, when looking at IL code produced by the compiler, I notice
that all methods on object variables use callvirt instead of call,
regardless of whether the method is truly virtual or not.

I assume this allows .NET to check if the object reference is null or
not before invoking the method. However, this seems very wasteful
when many methods are called on 1 object reference.

So, my questions are:
1) Can anyone quantify how much slower callvirt is than call?
2) Why couldn't the compiler check object reference for null on first
call, then replace all subsequent calls (when it knows the object ref
coudln't change) with IL call instead of IL callvirt?
3) If the overhead is at all significant, is there anyway to make the
compiler more efficient?

Don't forget that the JIT compiler is the piece of technology which
does most of the real optimisation. I'd expect it to cache the non-null
test etc.
 
Answers inline

--
Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://blogs.gotdotnet.com/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.
ThomasR said:
I understand that virtual methods on inherited objects are slower than
non-virtual methods because of the indirection required to support the
call.

However, when looking at IL code produced by the compiler, I notice
that all methods on object variables use callvirt instead of call,
regardless of whether the method is truly virtual or not.

I assume this allows .NET to check if the object reference is null or
not before invoking the method. However, this seems very wasteful
when many methods are called on 1 object reference.

So, my questions are:
1) Can anyone quantify how much slower callvirt is than call?

A tiny bit slower. It means there's a single extra test in your code.
2) Why couldn't the compiler check object reference for null on first
call, then replace all subsequent calls (when it knows the object ref
coudln't change) with IL call instead of IL callvirt?

Because, in the general case, the compiler cannot know this.
3) If the overhead is at all significant, is there anyway to make the
compiler more efficient?

I don't think th overhead is significant. We did some tests that showed the
effect was small even with trivial methods.

We have this behavior because we thought it was strange that you could call
a method on a null value and have it work (if it didn't refer to any
instance data).
 
We have this behavior because we thought it was strange that you could call
a method on a null value and have it work (if it didn't refer to any
instance data).


I understand that. What else is involved with callvirt vs. a simpler call ?
Does .NET (or at least C#) always use vtables even if the methods are not
virtual, cannot be overridden, and are compiled into the same assembly?

While we're on this subject, what about performances of interfaces ? Is
there more overhead to resolve these methods?
 
While we're on this subject, what about performances of interfaces ?
Is there more overhead to resolve these methods?

No there isn't. Interfaces are just a part of a vtable of a class. there is
no need for external lookup vtables or similar.

greets
Peter

--
------ooo---OOO---ooo------

Peter Koen - www.kema.at
MCAD MCDBA MCT
CAI/RS CASE/RS IAT

------ooo---OOO---ooo------
 
Hi,

Calling a method through an interface does however incur addition overhead.
In the JITed code it results in 2 extra move instructions, this can be
verified using either VS or cordbg. Note that I say through an interface,
what I mean here is when you have an interface reference to a concrete
object and invoke a interface method on this reference.
e.g..

IMyInterface c = new ClassImplementingIMyInterface(); // Get interface
reference to concrete object
c.DoSomething(); // Call a method through the interface reference, JIT code
results in 2 extra mov instructions, and if the concrete type is a ValueType
I suspect that this overhead would also incur additional boxing/unboxing
costs.

Happy holidays

Chris Taylor
 
Back
Top