如果您需要速度,请考虑在对象中嵌入“type(-identifying)number”,并使用switch语句选择类型特定的代码。这可以完全避免函数调用开销 - 只是进行本地跳转。你不会比这更快。成本(就可维护性,重新编译依赖性等而言)是强制特定类型功能的本地化(在交换机中)。
实现
#include <iostream>
#include <vector>
// virtual dispatch model...
struct Base
{
virtual int f() const { return 1; }
};
struct Derived : Base
{
virtual int f() const { return 2; }
};
// alternative: member variable encodes runtime type...
struct Type
{
Type(int type) : type_(type) { }
int type_;
};
struct A : Type
{
A() : Type(1) { }
int f() const { return 1; }
};
struct B : Type
{
B() : Type(2) { }
int f() const { return 2; }
};
struct Timer
{
Timer() { clock_gettime(CLOCK_MONOTONIC, &from); }
struct timespec from;
double elapsed() const
{
struct timespec to;
clock_gettime(CLOCK_MONOTONIC, &to);
return to.tv_sec - from.tv_sec + 1E-9 * (to.tv_nsec - from.tv_nsec);
}
};
int main(int argc)
{
for (int j = 0; j < 3; ++j)
{
typedef std::vector<Base*> V;
V v;
for (int i = 0; i < 1000; ++i)
v.push_back(i % 2 ? new Base : (Base*)new Derived);
int total = 0;
Timer tv;
for (int i = 0; i < 100000; ++i)
for (V::const_iterator i = v.begin(); i != v.end(); ++i)
total += (*i)->f();
double tve = tv.elapsed();
std::cout << "virtual dispatch: " << total << ' ' << tve << '\n';
// ----------------------------
typedef std::vector<Type*> W;
W w;
for (int i = 0; i < 1000; ++i)
w.push_back(i % 2 ? (Type*)new A : (Type*)new B);
total = 0;
Timer tw;
for (int i = 0; i < 100000; ++i)
for (W::const_iterator i = w.begin(); i != w.end(); ++i)
{
if ((*i)->type_ == 1)
total += ((A*)(*i))->f();
else
total += ((B*)(*i))->f();
}
double twe = tw.elapsed();
std::cout << "switched: " << total << ' ' << twe << '\n';
// ----------------------------
total = 0;
Timer tw2;
for (int i = 0; i < 100000; ++i)
for (W::const_iterator i = w.begin(); i != w.end(); ++i)
total += (*i)->type_;
double tw2e = tw2.elapsed();
std::cout << "overheads: " << total << ' ' << tw2e << '\n';
}
}
性能结果
在我的Linux系统:
~/dev g++ -O2 -o vdt vdt.cc -lrt
~/dev ./vdt
virtual dispatch: 150000000 1.28025
switched: 150000000 0.344314
overhead: 150000000 0.229018
virtual dispatch: 150000000 1.285
switched: 150000000 0.345367
overhead: 150000000 0.231051
virtual dispatch: 150000000 1.28969
switched: 150000000 0.345876
overhead: 150000000 0.230726
这表明联机型号码切换方法的速度约为(1.28 - 0.23)/(0.344 - 0.23)= 9.2倍。当然,这只针对确切的系统测试/编译器标志&版本等,但通常是指示性的。
评论RE虚拟监控调度
必须说,虽然虚拟函数调用的开销是东西是很少显著,并且只适用于经常被称为琐碎的功能(如getter和setter)。即使那样,你也许可以提供一个功能来同时获取和设置很多东西,从而最大限度地降低成本。人们担心虚拟调度的方式太多 - 所以在找到别扭的方案之前也要进行剖析。它们的主要问题是它们执行一个外联函数调用,尽管它们也会移除执行的代码,这会改变缓存利用率模式(为了更好或更常见)。
使用多态类实现它。调度虚拟功能的开销很可能是微不足道的。最糟糕的情况是,这很重要,而且你至少会有一个干净的设计,可以相对容易地进行优化。 – 2011-01-26 06:25:56