C ++中的dynamic_cast开销

时间:2011-09-08 14:39:44

标签: c++ casting

我知道dynamic_cast有运行时检查,因此考虑更安全(可以在失败时返回空指针)但比static_cast慢。但两者之间的开销有多糟糕?

我是否应该考虑在循环中使用static_cast来解决常规大型项目中的性能问题?或者差异很小,只与特殊的实时程序有关。

5 个答案:

答案 0 :(得分:11)

你有资料吗?

规则是:

  • 知道目标类型有效时,请使用static_cast
  • 当您不确定时使用dynamic_cast,并且您需要程序为您查找对象的运行时类型。

就这么简单。所有其他考虑都是次要的。

答案 1 :(得分:5)

取决于动态演员如何进行类安全/正确性检查。在我所描述的系统中,它可以很快变成非常大量的字符串比较。这是一个足够大的交易,我们几乎使用assert_cast样式系统,其中静态强制转换是为了性能而动态用于调试。

答案 2 :(得分:2)

Tomalak Geret'kal是对的,当你知道时static_cast,当你不知道时dynamic_cast。如果您想避免成本,您必须以您知道的方式构建您的设计。在单独的容器中存储单独的类型将使您的循环逻辑更复杂,但您可以使用模板算法来修复它。

对于简单的继承树,它非常快。如果你在具有虚拟继承的复杂层次结构中横向抛射,那么它必须进行一次重要的搜索。

示例:

struct Base {virtual ~Base () {}};
struct Foo : Base {};

struct Bar1 : virtual Base {};
struct Bar2 : virtual Base {};

struct Baz : Bar1, Bar2 {};

Base * a = new Foo ();
Bar1 * b = new Baz ();

dynamic_cast <Foo *> (a); // fast
dynamic_cast <Bar2 *> (b); // slow

性能在很大程度上取决于编译器。测量,测量,测量!请记住,运行时类型信息通常被排除在外并且将位于非本地内存中 - 您应该考虑缓存在循环中要执行的操作。

答案 3 :(得分:0)

非常大的C ++代码库(例如Mozilla,OpenOffice)习惯于禁用 RTTI(因此无法使用dynamic_cast和异常),因为仅仅包含的开销可执行文件中的RTTI数据被认为是不可接受的。特别是,据报道,由于额外的动态重定位,导致启动时间增加(我记得大约10%的数字)。

从未讨论过避免dynamic_cast和异常所需的额外代码实际上是否更慢。

答案 4 :(得分:0)

我刚尝试了一个小型的演员基准(在我3岁的上网本上,所以数字很高,但很好)。这是测试设置:

class A {
  public:
    virtual ~A() {}
};

class B : public A {
};

#define IT(DO) \
    for (unsigned i(1<<30); i; i--) { \
      B* volatile b(DO); \
      (void)b; \
    }

#define CastTest(CAST) IT(CAST<B*>(a))
#define NullTest() IT(NULL)

int main(int argc, char** argv) {
  if (argc < 2) {
    return 1;
  }
  A* a(new B());
  switch (argv[1][0]) {
    case 'd':
      CastTest(dynamic_cast)
      break;
    case 's':
      CastTest(static_cast)
      break;
    default:
      NullTest()
      break;
  }
  return 0;
}

我发现高度依赖于编译器优化,所以这是我的结果:

(参见下面的评估

00:

g++ -O0 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m7.139s
user        0m6.112s
sys         0m0.044s

real        0m8.177s
user        0m6.980s
sys         0m0.024s

real        1m38.107s
user        1m23.929s
sys         0m0.188s

O1:

g++ -O1 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m4.412s
user        0m3.868s
sys         0m0.032s

real        0m4.653s
user        0m4.048s
sys         0m0.000s

real        1m33.508s
user        1m21.209s
sys         0m0.236s

O2:

g++ -O2 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m4.526s
user        0m3.960s
sys         0m0.044s

real        0m4.862s
user        0m4.120s
sys         0m0.004s

real        0m2.835s
user        0m2.548s
sys         0m0.008s

O3:

g++ -O3 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m4.896s
user        0m4.308s
sys         0m0.004s

real        0m5.032s
user        0m4.284s
sys         0m0.008s

real        0m4.828s
user        0m4.160s
sys         0m0.008s

编辑:评估

对于一个演员(在上面的测试中,我们总共有2**30演员阵容),我们在上面的最小例子中得到以下时间:

-O0    71.66 ns
-O1    71.86 ns
-O2    -1.46 ns
-O3    -0.11 ns

负值可能是由于程序执行时的不同负载造成的,并且小到足以被丢弃为无意义(即== 0)。由于这里没有开销,我们必须假设编译器足够聪明以优化抛弃,即使我们说b是易变的。因此,唯一可靠的值是70 ns的结果。