Подтвердить что ты не робот

Накладные расходы dynamic_cast в С++

Я знаю, что dynamic_cast имеет проверку времени выполнения и поэтому считает более безопасным (может возвращать нулевой указатель при ошибке), но медленнее, чем static_cast. но насколько плохи накладные расходы между ними?

Должен ли я рассмотреть возможность использования static_cast в циклах для проблем с производительностью в обычных крупных проектах? или разница незначительна и применима только к специальным программам реального времени.

4b9b3361

Ответ 1

Вы прокомментировали это?

Правило:

  • Используйте static_cast, когда вы знаете, что тип цели действителен.
  • Используйте dynamic_cast, когда вы не уверены, и вам нужна программа для поиска типа выполнения объекта для вас.

Это так просто. Все остальные соображения являются вторичными.

Ответ 2

Зависит от того, как динамическая броска проверяет безопасность/правильность своего класса. В системах, которые я профилировал, он может превратиться в очень большое количество строк, которые очень быстро сравниваются. Это довольно серьезная сделка, в которой мы в значительной степени используем систему стиля assert_cast, в которой статическая броска выполняется для производительности, а динамическая - для отладки.

Ответ 3

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

Производительность будет сильно зависеть от компилятора. Измерение, измерение, измерение! Имейте в виду, что информация о типе времени выполнения обычно учитывается и будет находиться в нелокальной памяти - вы должны подумать о том, что кеш будет делать в циклах.

Ответ 4

Чрезвычайно большие С++-кодовые базы (например, Mozilla, OpenOffice) имеют привычку отключать RTTI (и, следовательно, не могут использовать dynamic_cast и исключения), потому что служебные данные, содержащие просто данные RTTI в исполняемом файле, считаются неприемлемыми. В частности, сообщается, что это приводит к увеличению (я помню номера порядка 10%) увеличения времени запуска из-за дополнительных динамических перемещений.

Независимо от того, нужен ли дополнительный код, чтобы избежать dynamic_cast, а исключения на самом деле еще медленнее, никогда не обсуждаются.

Ответ 5

Я только что опробовал небольшой бенчмарк (на моем ~ 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;
}

Я обнаружил, что он сильно зависит от оптимизации компилятора, поэтому вот мои результаты:

(см. ниже оценку)

O0:

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 нс.