Reputation: 60068
I wrote the following benchmark to estimate the overhead of virtual functions:
struct A{
int i = 0 ;
virtual void inc() __attribute__((noinline));
};
#ifdef VIRT
struct B : public A{
void inc() override __attribute__((noinline));
};
void A::inc() { }
void B::inc() { i++; }
#else
void A::inc() { i++; }
#endif
int main(){
#ifdef VIRT
B b;
A* p = &b;
#else
A a;
A* p = &a;
#endif
for( ;p->i < IT; p->inc()) {; }
return 0;
}
I compile it with
G=$((1000**3))
g++ -O1 -DIT=$((1*G)) -DVIRT virt.cc -o virt
g++ -O1 -DIT=$((1*G)) virt.cc -o nonvirt
And the results I got were that nonvirt was about 0.6ns slower than virt per function call at -O1
and about 0.3ns slower than virt at -O2
per function call.
How is this possible? I thought virtual functions were supposed to be slower.
Upvotes: 1
Views: 60
Reputation: 1639
EDIT: Has been corrected
Your main is wrong. the for loop is defined 2 times in one case and once in the other. This should not impact performance since the second time the loop exits immediately ?
Correct it like that :
int main(){
#ifdef VIRT
B b;
A* p = &b;
/* removed this for loop */
#else
A a;
A* p = &a;
#endif
for( ;p->i < IT; p->inc()) {; }
return 0;
}
Upvotes: 1
Reputation: 249153
First, just because you invoke a method through a pointer doesn't mean the compiler can't figure out the target type and make the call non-virtual. Plus, your program does nothing else, so everything will be well-predicted and in cache. Finally, a difference of 0.3 ns is one cycle, which is hardly worth noting. If you really want to dig into it, you could inspect the assembly code for each case on whatever your platform is.
On my system (Clang, OS X, old Macbook Air), the virtual case is a little slower, but it's hardly measurable with -O1
(e.g. 3.7 vs 3.6 seconds for non-virtual). And with -O2
there's no difference I can distinguish.
Upvotes: 4