每次调用负 0.6ns 的虚函数开销?

Virtual function overhead of negative 0.6ns per call?

我写了下面的基准来估计虚函数的开销:

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 的每个函数调用中,nonvirt 比 virt 大约 0.6ns ,大约 0.3ns 比 virt 每次函数调用 -O2

这怎么可能?我认为虚函数应该更慢。

首先,仅仅因为您通过指针调用方法并不意味着编译器无法确定目标类型并使调用成为非虚拟的。此外,您的程序不会执行任何其他操作,因此所有内容都将得到很好的预测并保存在缓存中。最后,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;
}