dynamic_cast 的性能?

新手上路,请多包涵

在阅读问题之前:

这个问题不是关于使用 dynamic_cast 有多大用处。它只是关于它的性能。

我最近开发了一种设计,其中经常使用 dynamic_cast

在与同事讨论时,几乎每个人都说 dynamic_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;

上面的代码在 Linux 上使用来自 boost::date_time 的方法来获取可用值。

我在一次执行中完成了 3 dynamic_cast ,测量它们的代码是相同的。

1次执行的结果如下:

Cast1 持续时间:74 微秒

Cast2 持续时间:2 微秒

Cast3 持续时间:1 微秒

第一次施法总是花费 74-111 微秒,同一执行中的以下施法花费 1-3 微秒。

所以最后我的问题:

dynamic_cast 真的表现不佳吗?

根据测试结果它不是。我的测试代码正确吗?

为什么这么多开发人员认为如果不是,它就很慢?

原文由 MOnsDaR 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

首先,您需要测量的性能不仅仅是几次迭代,因为您的结果将取决于计时器的分辨率。尝试例如 100 万+,以建立具有代表性的图片。此外,除非您将其与某些东西进行比较,即进行等效但没有动态转换,否则该结果是没有意义的。

其次,您需要通过优化同一个指针上的多个动态转换来确保编译器不会给您错误的结果(因此使用循环,但每次使用不同的输入指针)。

动态转换会比较慢,因为它需要访问对象的 RTTI(运行时类型信息)表,并检查转换是否有效。然后,为了正确使用它,您需要添加错误处理代码来检查返回的指针是否为 NULL 。所有这些都占用了周期。

我知道你不想谈论这个,但“一个经常使用 dynamic_cast 的设计”可能表明你做错了什么……

原文由 Oliver Charlesworth 发布,翻译遵循 CC BY-SA 2.5 许可协议

您的里程可能会有所不同,以低估情况。

dynamic_cast 的性能在很大程度上取决于你在做什么,并且可能取决于类的名称是什么(并且,比较时间相对于 reinterpet_cast 似乎很奇怪,因为在大多数情况下,它需要零指令实际目的,例如从 unsignedint 的演员表)。

我一直在研究它在 clang/g++ 中是如何工作的。 Assuming that you are dynamic_cast ing from a B* to a D* , where B is a (direct or indirect) base of 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
}

所以,是的,当 *p 实际上是 D 时,它非常快;只有一个成功 type_info 比较。最坏的情况是演员阵容失败,从 DB 有很多步骤;在这种情况下,有很多失败的类型比较。

类型比较需要多长时间?它在 clang/g++ 上执行此操作:

 compare_eq( type_info const &a, type_info const & b ){
   if( &a == &b) return true;   // same object
   return strcmp( a.name(), b.name())==0;
}

strcmp 是必需的,因为可能有两个不同的字符串对象为同一类型提供 type_info.name() (尽管我很确定这只发生在一个在共享库中,而另一个不在那个图书馆)。但是,在大多数情况下,当类型实际上相等时,它们引用相同的类型名称字符串;因此大多数 成功 的类型比较都非常快。

name() 方法只返回一个指针,指向一个包含类名的固定字符串。所以还有另一个因素:如果从 DB 的许多类的名称以 MyAppNameSpace::AbstractSyntaxNode< 开头,那么失败的比较将花费更长的时间比平常; strcmp 不会失败,直到它达到损坏的类型名称的差异。

而且,当然,由于整个操作是遍历一组表示类型层次结构的链接数据结构,时间将取决于这些东西是否在缓存中是新鲜的。因此,重复执行相同的演员表可能会显示平均时间,这不一定代表该演员表的典型表现。

原文由 greggo 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题