D_E*_*D_E 8 c++ performance dynamic-cast dispatch
我写了一个简单的例子,它使用基类接口和dynamic_cast以及非虚函数调用来估计调用虚函数的平均时间.就这个:
#include <iostream>
#include <numeric>
#include <list>
#include <time.h>
#define CALL_COUNTER (3000)
__forceinline int someFunction()
{
return 5;
}
struct Base
{
virtual int virtualCall() = 0;
virtual ~Base(){};
};
struct Derived : public Base
{
Derived(){};
virtual ~Derived(){};
virtual int virtualCall(){ return someFunction(); };
int notVirtualCall(){ return someFunction(); };
};
struct Derived2 : public Base
{
Derived2(){};
virtual ~Derived2(){};
virtual int virtualCall(){ return someFunction(); };
int notVirtualCall(){ return someFunction(); };
};
typedef std::list<double> Timings;
Base* createObject(int i)
{
if(i % 2 > 0)
return new Derived();
else
return new Derived2();
}
void callDynamiccast(Timings& stat)
{
for(unsigned i = 0; i < CALL_COUNTER; ++i)
{
Base* ptr = createObject(i);
clock_t startTime = clock();
for(int j = 0; j < CALL_COUNTER; ++j)
{
Derived* x = (dynamic_cast<Derived*>(ptr));
if(x) x->notVirtualCall();
}
clock_t endTime = clock();
double callTime = (double)(endTime - startTime) / CLOCKS_PER_SEC;
stat.push_back(callTime);
delete ptr;
}
}
void callVirtual(Timings& stat)
{
for(unsigned i = 0; i < CALL_COUNTER; ++i)
{
Base* ptr = createObject(i);
clock_t startTime = clock();
for(int j = 0; j < CALL_COUNTER; ++j)
ptr->virtualCall();
clock_t endTime = clock();
double callTime = (double)(endTime - startTime) / CLOCKS_PER_SEC;
stat.push_back(callTime);
delete ptr;
}
}
int main()
{
double averageTime = 0;
Timings timings;
timings.clear();
callDynamiccast(timings);
averageTime = (double) std::accumulate<Timings::iterator, double>(timings.begin(), timings.end(), 0);
averageTime /= timings.size();
std::cout << "time for callDynamiccast: " << averageTime << std::endl;
timings.clear();
callVirtual(timings);
averageTime = (double) std::accumulate<Timings::iterator, double>(timings.begin(), timings.end(), 0);
averageTime /= timings.size();
std::cout << "time for callVirtual: " << averageTime << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
看起来callDynamiccast几乎要花费两倍多.
time for callDynamiccast: 0.000240333
time for callVirtual: 0.0001401
任何想法为什么呢?
编辑:对象创建现在是在separete函数中创建的,因此编译器不知道它是真实类型.几乎相同的结果.
EDITED2:创建两种不同类型的派生对象.
jus*_*tin 12
虚函数调用类似于函数指针,或者如果编译器知道类型,则静态调度.这是恒定的时间.
dynamic_cast完全不同 - 它使用实现定义的方法来确定类型.它不是常量时间,可以遍历类层次结构(也考虑多重继承)并执行多次查找.实现可以使用字符串比较.因此,复杂性在两个维度上更高.dynamic_cast由于这些原因,实时系统通常会避免/不鼓励.
应该注意的是,虚函数的全部目的是不必取消继承图。存在虚拟函数,因此您可以像使用基类一样使用派生类实例。这样就可以从最初称为基类版本的代码中调用更专业的功能实现。
如果虚拟函数比安全地强制转换为派生类+函数调用慢,则C ++编译器将简单地以这种方式实现虚拟函数调用。
因此,没有理由期望dynamic_cast+ call更快。
您只是在衡量 的成本dynamic_cast<>。它是用 RTTI 实现的,这在任何 C++ 编译器中都是可选的。项目 + 属性、C/C++、语言、启用运行时类型信息设置。将其更改为否。
您现在会收到一个dynamic_cast<>无法再正常工作的简单提醒。随意更改它以static_cast<>获得截然不同的结果。这里的关键点是,如果您知道向上转换总是安全的,那么static_cast<>就会为您购买您正在寻找的性能。如果你不知道upcast 是安全的,那么你就不会dynamic_cast<>遇到麻烦。这是一种非常难以诊断的问题。常见的失败模式是堆损坏,如果您真的很幸运,您只会立即获得 GPF。
| 归档时间: |
|
| 查看次数: |
5459 次 |
| 最近记录: |