Производительность dynamic_cast? - PullRequest
39 голосов
/ 29 октября 2010

Перед прочтением вопроса:
Этот вопрос не о том, насколько полезно использовать 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;

Приведенный выше код использует методы из boost::date_time в Linux для получения используемых значений.
Я сделал 3 dynamic_cast в одномвыполнение, код для их измерения такой же.

Результаты 1 выполнения были следующими:
Cast1 длится: 74 мкс
Cast2 длится: 2 мкс
Cast3 длится: 1 мкс

Первое приведение всегда занимало 74-111 мкс, последующее приведение в одном и том же исполнении занимало 1-3 мкс.

Итак, наконец, мои вопросы:
dynamic_cast действительно плохо работает?
По результатам теста его нет.Мой тестовый код правильный?
Почему так много разработчиков считают, что он медленный, если нет?

Ответы [ 4 ]

49 голосов
/ 29 октября 2010

Во-первых, вам нужно измерить производительность гораздо больше, чем всего за несколько итераций, так как ваши результаты будут зависеть от разрешения таймера. Попробуйте, например, 1 миллион +, чтобы создать репрезентативную картину. Кроме того, этот результат не имеет смысла, если вы не сравниваете его с чем-то, то есть делаете эквивалент, но без динамического приведения.

Во-вторых, вам нужно убедиться, что компилятор не дает ложных результатов, оптимизируя несколько динамических приведений к одному и тому же указателю (поэтому используйте цикл, но каждый раз используйте другой указатель ввода).

Динамическое приведение будет медленнее, потому что оно должно получить доступ к таблице RTTI (информация о типе времени выполнения) для объекта и проверить, является ли приведение действительным. Затем, чтобы правильно его использовать, вам нужно будет добавить код обработки ошибок, который проверяет, равен ли возвращаемый указатель NULL. Все это занимает циклы.

Я знаю, что вы не хотели говорить об этом, но "дизайн, в котором часто используется dynamic_cast", вероятно, является индикатором того, что вы делаете что-то не так ...

28 голосов
/ 29 октября 2010

Производительность не имеет смысла без сравнения эквивалентных функциональных возможностей. Большинство людей говорят, что dynamic_cast медленный без сравнения с эквивалентным поведением.Назовите их на это.Другими словами:

Если «работа» не является обязательным требованием, я могу написать код, который дает сбой быстрее, чем ваш.

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

Однако без использования dynamic_cast будет всегда будет быстрее, чем его использовать - но если вам на самом деле не нужен dynamic_cast, тогда не используйте его!Если вам нужен динамический поиск, тогда будут некоторые издержки, и вы сможете сравнить различные стратегии.

17 голосов
/ 28 сентября 2011

Вот несколько тестов:
http://tinodidriksen.com/2010/04/14/cpp-dynamic-cast-performance/
http://www.nerdblog.com/2006/12/how-slow-is-dynamiccast.html

По их словам, dynamic_cast в 5-30 раз медленнее, чем reinterpret_cast, и лучшая альтернатива работает почти так же, как reinterpret_cast.

Я приведу заключение из первой статьи:

  • dynamic_cast медленный для всего, кроме приведения к базовому типу; тот конкретный состав оптимизирован
  • уровень наследования оказывает большое влияние на dynamic_cast
  • переменная-член + reinterpret_cast - самый быстрый и надежный способ
    определить тип; однако, это имеет намного более высокие накладные расходы на техническое обслуживание
    при кодировании

Абсолютные числа порядка 100 нс для одного броска. Такие значения, как 74 мсек, не кажутся близкими к реальности.

6 голосов
/ 29 октября 2010

Извините, что говорю это, но ваш тест практически бесполезен для определения того, медленный ли бросок или нет. Разрешение микросекунды далеко не достаточно хорошее. Мы говорим об операции, которая, даже в худшем случае, не должна занимать более, скажем, 100 тактов или менее 50 наносекунд на типичном ПК.

Нет никаких сомнений в том, что динамическое приведение будет медленнее, чем статическое приведение или повторное толкование, поскольку на уровне сборки последние два равны назначению (очень быстрое, порядка 1 такта) Динамическое приведение требует, чтобы код пошел и проверил объект, чтобы определить его реальный тип.

Я не могу сказать, насколько медленно это происходит на самом деле, это может варьироваться от компилятора к компилятору, мне нужно увидеть код сборки, сгенерированный для этой строки кода. Но, как я уже сказал, 50 наносекунд на звонок - это верхний предел того, что ожидается разумным.

...