C++ 动态_cast的性能?
在阅读问题之前:C++ 动态_cast的性能?,c++,performance,dynamic-cast,boost-date-time,C++,Performance,Dynamic Cast,Boost Date Time,在阅读问题之前: 这个问题不是关于使用dynamic\u cast有多有用。这就是它的性能 我最近开发了一个设计,其中大量使用了dynamic\u cast。 在与同事讨论时,几乎每个人都说不应该使用dynamic\u cast,因为它的性能很差(这些同事有不同的背景,有时彼此不认识。我在一家大公司工作) 我决定测试这种方法的性能,而不是仅仅相信它们 使用了以下代码: ptime firstValue( microsec_clock::local_time() ); ChildObject*
这个问题不是关于使用
dynamic\u cast
有多有用。这就是它的性能
我最近开发了一个设计,其中大量使用了dynamic\u cast
。在与同事讨论时,几乎每个人都说不应该使用
dynamic\u cast
,因为它的性能很差(这些同事有不同的背景,有时彼此不认识。我在一家大公司工作)
我决定测试这种方法的性能,而不是仅仅相信它们
使用了以下代码:
ptime firstValue( microsec_clock::local_time() );
ChildObject* castedObject = dynamic_cast<ChildObject*>(parentObject);
ptime secondValue( microsec_clock::local_time() );
time_duration diff = secondValue - firstValue;
std::cout << "Cast1 lasts:\t" << diff.fractional_seconds() << " microsec" << std::endl;
ptimefirstvalue(microsec_clock::local_time());
ChildObject*castedObject=dynamic\u cast(父对象);
ptime秒值(微秒时钟::本地时间());
时间\持续时间差=第二个值-第一个值;
std::cout首先,您需要测量的性能远远超过几个迭代,因为您的结果将由计时器的分辨率决定。尝试例如100万以上,以建立一个具有代表性的图片。此外,除非您将此结果与其他内容进行比较,否则此结果是没有意义的,例如,执行等效操作,但不执行动态强制转换
其次,您需要通过优化同一指针上的多个动态强制转换来确保编译器不会给出错误的结果(因此使用循环,但每次使用不同的输入指针)
动态强制转换将较慢,因为它需要访问对象的RTTI(运行时类型信息)表,并检查强制转换是否有效。然后,为了正确使用它,您需要添加错误处理代码来检查返回的指针是否为NULL
。所有这些都需要周期
我知道你不想谈论这个,但“一个经常使用动态强制转换的设计”可能是一个指标,表明你做错了什么…如果不比较等效的功能,性能是毫无意义的。大多数人说,如果不比较等效的行为,动态强制转换是缓慢的。就这件事给他们打电话。换句话说:
如果“有效”不是一个要求,我可以编写比你更快失败的代码
有多种方法可以实现动态_cast,有些方法比其他方法更快。例如,斯特劳斯特鲁普发表了一篇关于使用的论文。不幸的是,控制编译器如何实现强制转换并不常见,但如果性能对您来说真的很重要,那么您可以控制使用哪个编译器
然而,不使用dynamic_cast总是比使用它快-但是如果您实际上不需要dynamic_cast,那么就不要使用它!如果您确实需要动态查找,那么会有一些开销,然后您可以比较各种策略。很抱歉这么说,但是您的测试对于确定强制转换是否缓慢几乎是无用的。微秒的分辨率远远不够好。我们讨论的是一种操作,即使在最坏的情况下,在一台典型的PC上也不应该超过,比如说,100个时钟滴答,或者少于50纳秒
毫无疑问,动态强制转换将比静态强制转换或重新解释强制转换慢,因为在程序集级别,后两种转换相当于赋值(非常快,顺序为1个时钟周期),动态强制转换需要代码去检查对象以确定其实际类型
我不能马上说它到底有多慢,这可能会因编译器而异,我需要查看为这行代码生成的汇编代码。但是,正如我所说,每次呼叫50纳秒是合理的上限。这里有一些基准:
根据他们的说法,dynamic_cast比reinterpret_cast慢5-30倍,而最佳替代方案的性能几乎与reinterpret_cast相同
我将引用第一篇文章的结论:
- 动态_cast对于任何东西来说都很慢,但对基本类型的转换除外;那个
对特定的铸件进行了优化
- 继承级别对动态转换有很大影响
- 成员变量+重新解释\u cast是实现
确定类型;但是,这会带来更高的维护开销
编码时
单次投射的绝对数约为100纳秒。像74毫秒这样的数值似乎与实际情况不符。您的里程数可能会有所不同,这可能低估了情况
动态\u cast的性能在很大程度上取决于您正在做什么,也可能取决于类的名称(而且,与reinterpet\u cast
相比,时间似乎有些奇怪,因为在大多数情况下,出于实际目的,它不需要任何指令,例如从无符号
到int
的转换)
我一直在研究它在clang/g++中是如何工作的。假设您是dynamic\u cast
从B*
转换为D*
,其中B
是D
的一个(直接或间接)基,并且不考虑多个基类的复杂情况,它似乎通过调用一个库函数来工作,该函数执行以下操作:
for dynamic_cast<D*>( p ) where p is B*
type_info const * curr_typ = &typeid( *p );
while(1) {
if( *curr_typ == typeid(D)) { return static_cast<D*>(p); } // success;
if( *curr_typ == typeid(B)) return nullptr; //failed
curr_typ = get_direct_base_type_of(*curr_typ); // magic internal operation
}
strcmp是必需的,因为可以有两个不同的type\u info
对象来表示相同的类型(尽管我很确定这只发生在一个在共享库中,另一个不在该库中的情况下)。但是,在大多数情况下,当类型实际上相等时,它们引用相同的类型信息;因此,最成功的类型比较非常快
name()。
因此还有另一个因素:如果从D
到B
的许多类的名称都以MyAppNameSpace::AbstractSyntaxNode开头,那么问题就没有提到替代方案。
在RTTI广泛使用之前,或只是为了避免使用RTTI,
compare_eq( type_info const &a, type_info const & b ){
if( &a == &b) return true; // same object
return strcmp( a.name(), b.name())==0;
}