C++ 虚拟函数的开销测试

C++ 虚拟函数的开销测试,c++,performance,optimization,virtual,overhead,C++,Performance,Optimization,Virtual,Overhead,我设置了一个(可能是非常不科学的)小测试来确定单级单继承中虚拟函数的开销,当以多态方式访问派生类或直接访问派生类时,我得到的结果完全相同。有点令人惊讶的是,当任何函数被声明为虚函数时,所引入的计算时间的数量级(见下面的结果) 在声明成员函数时是否会有如此大的开销?为什么即使直接访问派生类,它仍然存在 代码如下: class base { public: virtual ~base() {} virtual uint func(uint i) = 0; }; class deri

我设置了一个(可能是非常不科学的)小测试来确定单级单继承中虚拟函数的开销,当以多态方式访问派生类或直接访问派生类时,我得到的结果完全相同。有点令人惊讶的是,当任何函数被声明为虚函数时,所引入的计算时间的数量级(见下面的结果)

在声明成员函数时是否会有如此大的开销?为什么即使直接访问派生类,它仍然存在

代码如下:

class base
{
public:
    virtual ~base() {}
    virtual uint func(uint i) = 0;
};

class derived : public base
{
public:
    ~derived() {}
    uint func(uint i) { return i * 2; }
};

uint j = 0;
ulong k = 0;
double l = 0;
ushort numIters = 10;
base* mybase = new derived;  // or derived* myderived = ...

for(ushort i = 0; i < numIters; i++)
{
  clock_t start2, finish2;
  start2 = clock();

  for (uint j = 0; j < 100000000; ++j)
        k += mybase->func(j);

  finish2 = clock();
  l += (double) (finish2 - start2);
  std::cout << "Total duration: " << (double) (finish2 - start2) << " ms." << std::endl;

}

std::cout << "Making sure the loop is not optimized to nothing: " << k << std::endl;
std::cout << "Average duration: " << l / numIters << " ms." << std::endl;
类基
{
公众:
虚拟~base(){}
虚单位函数(单位i)=0;
};
派生类:公共基
{
公众:
~derived(){}
uint func(uint i){返回i*2;}
};
uint j=0;
ulong k=0;
双l=0;
ushort numIters=10;
base*mybase=新派生;//或派生*myderived=。。。
对于(ushort i=0;ifunc(j);
finish2=时钟();
l+=(双)(完成2-开始2);
std::cout直接访问它与间接访问它做同样的工作

当您在
myderived
上调用函数时,存储在那里的指针可能指向从
derived
派生的某个类的某个对象。编译器不能假定它确实是
派生的
对象,它可能是重写虚拟函数的另一个派生类的对象,因此需要虚拟f函数分派与mybase中的函数分派类似。在这两种情况下,函数在调用前都会在虚拟函数表中查找

要以非多态方式调用函数,请不要使用指针:

derived myderived;
myderived.func(1); 
当您删除虚拟函数时,编译器可以内联函数调用,这样您基本上会得到一个简单的循环:

for (uint j = 0; j < 100000000; ++j)
    k += i * 2;
for(uint j=0;j<100000000;++j)
k+=i*2;
这要快得多,因为您节省了100000000个函数调用的开销,而且编译器甚至可以进一步优化循环,如果循环中有函数调用的话,就不会这样了


还要注意的是,如果函数做了一些实际工作,内联版本和虚拟函数调用之间的差异就会小得多。在本例中,函数体几乎不花费任何时间,因此调用函数的成本大于执行函数体的成本。

虚拟函数基本上不需要任何成本。大多数真实函数性能问题是由不必要的繁杂调用树造成的,这些调用树所做的事情是您永远猜不到的


我找到它们的方法是在调试器下暂停应用程序几次,并检查使用该方法获得43倍加速的状态,包括调用堆栈。

似乎您正在同时计算继承+虚拟函数的成本。您应该测试在没有任何虚拟函数的情况下实例化派生类,以及also基类。为什么要使用
new
?在堆栈上实例化对象会更简单…在这种情况下,这绝对不会有什么不同。如果通过指针访问,则会产生相同的开销。我明白了,谢谢。你说的很有道理。同意你的观点,函数的工作量太少。比例明显有偏差这里,但知道函数调用开销是很好的。因此,基本上,在一天结束时,一旦你声明一个函数为虚拟函数并通过指针使用它,你几乎已经付出了使用虚拟函数的全部代价。s/编译器不能假设它真的是派生的/编译器不假设它真的是派生的/这是可能的,是吗因为没有更多派生类(注:需要整个程序优化)我试图删除指针,但速度比使用它慢。@YohaiïBerreby您需要实际记录您所做的操作,以获得对其速度较慢原因的解释。这可能是多种原因——但在最简单的层面上,如果操作错误,每次迭代都可能会生成对象副本/切片,而指针则不会容易受到影响这将是代码中的一个问题,不支持/反对静态或动态调度。谢谢Mike,这是一个很棒的方法。