在您畏缩重复标题之前,另一个问题不适合我在这里提出的要求(IMO)。所以。

我真的很想在我的应用程序中使用虚函数使事情变得简单一百倍(不是OOP的全部内容;)。但是我在某处读到它们是以性能为代价的,除了看到过时的人为过早的优化而进行的大肆宣传外,我什么都没有看到,我决定在一个小型基准测试中使用以下方法进行快速测试:

CProfiler.cpp

#include "CProfiler.h"

CProfiler::CProfiler(void (*func)(void), unsigned int iterations) {
    gettimeofday(&a, 0);
    for (;iterations > 0; iterations --) {
        func();
    }
    gettimeofday(&b, 0);
    result = (b.tv_sec * (unsigned int)1e6 + b.tv_usec) - (a.tv_sec * (unsigned int)1e6 + a.tv_usec);
};

main.cpp
#include "CProfiler.h"

#include <iostream>

class CC {
  protected:
    int width, height, area;
  };

class VCC {
  protected:
    int width, height, area;
  public:
    virtual void set_area () {}
  };

class CS: public CC {
  public:
    void set_area () { area = width * height; }
  };

class VCS: public VCC {
  public:
    void set_area () {  area = width * height; }
  };

void profileNonVirtual() {
    CS *abc = new CS;
    abc->set_area();
    delete abc;
}

void profileVirtual() {
    VCS *abc = new VCS;
    abc->set_area();
    delete abc;
}

int main() {
    int iterations = 5000;
    CProfiler prf2(&profileNonVirtual, iterations);
    CProfiler prf(&profileVirtual, iterations);

    std::cout << prf.result;
    std::cout << "\n";
    std::cout << prf2.result;

    return 0;
}

起初,我只进行了100和10000次迭代,结果令人担忧:非虚拟化为4毫秒,虚拟化为250毫秒!我差点进入内部,但后来又将迭代次数提高到了500,000次。即可看到结果几乎完全相同(如果未启用优化标志,则速度可能会降低5%)。

我的问题是,为什么迭代次数少而迭代次数却多呢?纯粹是因为虚函数在这么多次迭代中在缓存中很热吗?

免责声明
我了解我的“概要分析”代码不是完美的,但是它已经给出了对事物的估计,这是在此级别上最重要的事情。我也问这些问题要学习,而不是仅仅优化我的应用程序。

最佳答案

扩展Charles' answer

这里的问题是您的循环所做的不只是测试虚拟调用本身(无论如何,内存分配可能会使虚拟调用的开销相形见)),因此他的建议是更改代码,以便仅测试虚拟调用。

这里的基准功能是模板,因为可以内联模板,而不太可能通过函数指针进行调用。

template <typename Type>
double benchmark(Type const& t, size_t iterations)
{
  timeval a, b;
  gettimeofday(&a, 0);
  for (;iterations > 0; --iterations) {
    t.getArea();
  }
  gettimeofday(&b, 0);
  return (b.tv_sec * (unsigned int)1e6 + b.tv_usec) -
         (a.tv_sec * (unsigned int)1e6 + a.tv_usec);
}

类(class):
struct Regular
{
  Regular(size_t w, size_t h): _width(w), _height(h) {}

  size_t getArea() const;

  size_t _width;
  size_t _height;
};

// The following line in another translation unit
// to avoid inlining
size_t Regular::getArea() const { return _width * _height; }

struct Base
{
  Base(size_t w, size_t h): _width(w), _height(h) {}

  virtual size_t getArea() const = 0;

  size_t _width;
  size_t _height;
};

struct Derived: Base
{
  Derived(size_t w, size_t h): Base(w, h) {}

  virtual size_t getArea() const;
};

// The following two functions in another translation unit
// to avoid inlining
size_t Derived::getArea() const  { return _width * _height; }

std::auto_ptr<Base> generateDerived()
{
  return std::auto_ptr<Base>(new Derived(3,7));
}

和测量:
int main(int argc, char* argv[])
{
  if (argc != 2) {
    std::cerr << "Usage: %prog iterations\n";
    return 1;
  }

  Regular regular(3, 7);
  std::auto_ptr<Base> derived = generateDerived();

  double regTime = benchmark<Regular>(regular, atoi(argv[1]));
  double derTime = benchmark<Base>(*derived, atoi(argv[1]));

  std::cout << "Regular: " << regTime << "\nDerived: " << derTime << "\n";

  return 0;
}

注意:与常规函数相比,这测试了虚拟调用的开销。功能有所不同(由于在第二种情况下没有运行时分派(dispatch)),因此这是最坏情况下的开销。

编辑:

运行结果(gcc.3.4.2,-O2,SLES10四核服务器)注意:函数定义位于另一个转换单元中,以防止内联
> ./test 5000000
Regular: 17041
Derived: 17194

不太令人信服。

关于c++ - 虚函数和性能C++,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4871954/

10-13 09:01