在您畏缩重复标题之前,另一个问题不适合我在这里提出的要求(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/