我编写了以下基准来估计虚拟函数的开销:
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
我得到的结果是,在-O1
的每个函数调用中,non-virt比virt慢0.6ns,在-O2
的每个函数呼叫中,non-virt比virt慢0.3ns。
这怎么可能?我认为虚拟功能应该更慢。
首先,通过指针调用方法并不意味着编译器无法确定目标类型并使调用成为非虚拟调用。另外,你的程序不做任何其他事情,所以一切都会被很好地预测并缓存。最后,0.3 ns的差异是一个周期,这几乎不值得注意。如果你真的想深入研究,你可以在任何平台上检查每个案例的汇编代码
在我的系统(Clang、OS X、旧Macbook Air)上,虚拟机箱的速度稍慢,但使用-O1
几乎无法测量(例如,非虚拟机箱为3.7秒,而非虚拟机箱则为3.6秒)。和-O2
没有区别。
你的主菜错了。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;
}