Накладные расходы dynamic_cast в С++
Я знаю, что dynamic_cast имеет проверку времени выполнения и поэтому считает более безопасным (может возвращать нулевой указатель при ошибке), но медленнее, чем static_cast. но насколько плохи накладные расходы между ними?
Должен ли я рассмотреть возможность использования static_cast в циклах для проблем с производительностью в обычных крупных проектах? или разница незначительна и применима только к специальным программам реального времени.
Ответы
Ответ 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 нс.