我编写了以下基准来估算虚函数的开销:
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;
}
我用
编译它G=$((1000**3))
g++ -O1 -DIT=$((1*G)) -DVIRT virt.cc -o virt
g++ -O1 -DIT=$((1*G)) virt.cc -o nonvirt
我得到的结果是,nonvirt比-O1
的每个函数调用的函数大约0.6ns 慢,而-O2
的函数调用大于0.3ns 。每个函数调用{1}}。
这怎么可能?我认为虚拟功能应该更慢。
答案 0 :(得分:4)
首先,仅仅因为通过指针调用方法并不意味着编译器无法找出目标类型并使调用成为非虚拟。此外,您的程序不会执行任何其他操作,因此一切都将得到很好的预测和缓存。最后,0.3 ns的差异是一个周期,这几乎不值得注意。如果您真的想深入了解它,您可以在任何平台上检查每个案例的汇编代码。
在我的系统上(Clang,OS X,旧的Macbook Air),虚拟情况稍微慢一点,但-O1
几乎无法衡量(例如非虚拟情况下为3.7 vs 3.6秒)。使用-O2
,我可以区分无差异。
答案 1 :(得分:1)
你的主要是错的。 for循环在一种情况下定义2次,在另一种情况下定义一次。这不应该影响性能,因为第二次循环立即退出?
纠正它:
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;
}