Удар по производительности из-за приведения стилей C++?

Я новичок в приведении типов C++ и обеспокоен тем, что использование приведений стилей C++ приведет к снижению производительности моего приложения, поскольку у меня есть критический крайний срок в реальном времени в моей процедуре обслуживания прерываний.

Я слышал, что некоторые приведения даже вызывают исключения!

Я хотел бы использовать приведения в стиле С++, потому что это сделало бы мой код более "надежным". Однако, если производительность снижается, я, вероятно, не буду использовать приведения типов в стиле C++, а вместо этого потрачу больше времени на тестирование кода, использующего приведения в стиле C.


Кто-нибудь проводил тщательное тестирование/профилирование, чтобы сравнить производительность приведения стилей С++ с приведениями в стиле C?

Каковы были ваши результаты?

Какие выводы вы сделали?


person Trevor Boyd Smith    schedule 23.03.2009    source источник
comment
Возможно, мне придется добавить награду к этому... вот насколько я заинтересован.   -  person Trevor Boyd Smith    schedule 23.03.2009
comment
У вас уже было два точных (если я сам так говорю) ответа.   -  person    schedule 23.03.2009
comment
Текущие ответы здесь показывают, что static_cast в стиле С++ не имеет производительности во время выполнения. Кто-нибудь, пожалуйста, прокомментируйте или добавьте объяснение, почему у них будет такая же производительность? (Вопрос намеренно расплывчатый.)   -  person Trevor Boyd Smith    schedule 23.03.2009
comment
Ну, а почему бы тебе самому не измерить? Это тривиально легко сделать.   -  person Brian Neal    schedule 23.03.2009
comment
Почему бы им не иметь такую ​​же производительность? Единственный, у которого есть какие-либо дополнительные затраты времени выполнения, это dynamic_cast, и вы все равно не можете сделать это в C.   -  person Brian Neal    schedule 23.03.2009
comment
Потому что они делают то же самое   -  person    schedule 23.03.2009
comment
Обновил мой ответ. Теперь он цитирует главу и стих из стандарта. Они, как сказал Нил, делают одно и то же.   -  person jalf    schedule 23.03.2009
comment
переинтерпретировать и статические приведения делают то же самое, что и приведения в стиле C - разница в том, что статическое не позволит вам выполнять «небезопасные» приведения, тогда как повторное толкование отключает безопасность. Grep'ing для приведений в стиле С++ = ‹3 по сравнению со стилем c :)   -  person snemarch    schedule 24.03.2009
comment
@snemarch: добавление к этому оператору отключает безопасность на этапе компиляции. Выдает идентичный код. static_cast просто придирчиво относится к тому, вызовет ли это ошибку компилятора или нет.   -  person jmucchiello    schedule 23.12.2009
comment
@jmucchiello: Старый вопрос, но я наткнулся на него и хочу прояснить это для будущих читателей: static_cast и reinterpret_cast НЕ всегда будут создавать один и тот же код среды выполнения, и на самом деле очень мало совпадений в том, какие преобразования возможны для обоих приведений. Например: вы не можете использовать reinterpret_cast для преобразования из числа с плавающей запятой в целое число (но `из double* в int*), и когда вы используете для этого static_cast, это не бесплатно, но на самом деле потребует выполнения одной или нескольких инструкций (как если бы приведение в стиле c). В случае классов static_cast может даже вызвать вызов функции   -  person MikeMB    schedule 22.04.2016


Ответы (7)


Если приведение в стиле C++ может быть концептуально заменено приведением в стиле C, накладных расходов не будет. Если это невозможно, как в случае с dynamic_cast, для которого нет эквивалента C, вы должны так или иначе оплатить стоимость.

В качестве примера следующий код:

int x;
float f = 123.456;

x = (int) f;
x = static_cast<int>(f);

генерирует идентичный код для обоих приведений с VC++ - код:

00401041   fld         dword ptr [ebp-8]
00401044   call        __ftol (0040110c)
00401049   mov         dword ptr [ebp-4],eax

Единственное приведение C++, которое может генерировать, — это dynamic_cast при приведении к ссылке. Чтобы избежать этого, выполните приведение к указателю, который вернет 0 в случае неудачного приведения.

person Community    schedule 23.03.2009
comment
Приведение в стиле C++ никогда не заменяется приведением в стиле C. Если что, то наоборот. Стандарт определяет только поведение приведений в стиле C++. Описания в стиле C описаны в терминах C++. - person jalf; 23.03.2009
comment
@jalf да - я имел в виду заменить концептуально - person ; 23.03.2009
comment
Да, но, возможно, стоит прояснить это на 110%, если ОП примет это. ;) - person jalf; 23.03.2009
comment
Но листинг на ассемблере... листинг на ассемблере!!! Чего он хочет, крови? :) - person Daniel Earwicker; 23.03.2009
comment
Отличный ответ, концептуальное сравнение точное. - person Matt Joiner; 15.11.2010

Единственным, требующим каких-либо дополнительных затрат во время выполнения, является dynamic_cast, у которого есть возможности, которые в любом случае нельзя воспроизвести напрямую с помощью приведения в стиле C. Так что у вас нет проблем.

Самый простой способ убедиться в этом — поручить вашему компилятору сгенерировать вывод на ассемблере и изучить сгенерированный им код. Например, в любом разумно реализованном компиляторе reinterpret_cast вообще исчезнет, ​​потому что это просто означает «идти вслепую и притворяться, что данные относятся к этому типу».

person Daniel Earwicker    schedule 23.03.2009

Почему должен быть хит производительности? Они выполняют точно те же функции, что и приведения C. Единственное отличие состоит в том, что они обнаруживают больше ошибок во время компиляции и их легче искать в исходном коде.

static_cast<float>(3) полностью эквивалентен (float)3 и будет генерировать точно такой же код.

Учитывая float f = 42.0f, reinterpret_cast<int*>(&f) точно эквивалентно (int*)&f и будет генерировать точно такой же код.

И так далее. Единственное отличие — это dynamic_cast, которое, да, может генерировать исключение. Но это потому, что он делает вещи, которые не могут делать актеры в стиле C. Так что не используйте dynamic_cast, если вам не нужна его функциональность.

Обычно можно с уверенностью предположить, что составители компиляторов умны. Имея два разных выражения, которые имеют одинаковую семантику в соответствии со стандартом, обычно можно с уверенностью предположить, что они будут одинаково реализованы в компиляторе.

К сожалению. Второй пример должен быть, конечно, reinterpret_cast, а не dynamic_cast. Исправлено сейчас.

Хорошо, просто чтобы было абсолютно ясно, вот что говорит стандарт C++:

§5.4.5:

Преобразования, выполненные

  • a const_cast (5.2.11)
  • a static_cast (5.2.9)
  • static_cast, за которым следует const_cast
  • a reinterpret_cast (5.2.10), or
  • reinterpret_cast, за которым следует const_cast.

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

Таким образом, что угодно, так как приведение в стиле C реализовано в терминах приведения C++, приведение в стиле C должно быть медленнее. (Конечно, это не так, потому что компилятор в любом случае генерирует один и тот же код, но это более правдоподобно, чем более медленное приведение типов в стиле C++.)

person jalf    schedule 23.03.2009
comment
Учитывая ваш комментарий: обычно можно с уверенностью предположить, что они будут реализованы в компиляторе одинаково. Выполняли ли вы какую-либо встроенную работу или работу в режиме реального времени? - person Trevor Boyd Smith; 23.03.2009
comment
dynamic_cast будет генерировать только при работе со ссылками. Если он работает с указателем, он вернет 0, если приведение не удастся. И с какой стати вам использовать dynamic_cast для преобразования float* в int*? Это бессмысленно. - person Brian Neal; 23.03.2009
comment
о, упс, я имел в виду reinterpret_cast, конечно. Теперь это исправлено. Спасибо, что указали на это. :) - person jalf; 23.03.2009
comment
+1. ха-ха 5.4.5 было первым, что пришло мне в голову, когда я прочитал этот вопрос :D - person Johannes Schaub - litb; 24.03.2009
comment
О господи, только не говорите мне, что вы могли вспомнить, что это была версия 5.4.5... Пришлось поискать! ;) - person jalf; 24.03.2009
comment
ха-ха, нет, не бойся. я просто знал, что приведения в стиле C определяются в терминах стилей C++: p Мне не нравится запоминать такие номера разделов :) - person Johannes Schaub - litb; 24.03.2009
comment
@litb: кстати, я не думаю, что вы могли бы пролить свет на tinyurl.com/c9daz8? ;) - person jalf; 24.03.2009

Существует четыре приведения в стиле C++:

  • const_cast
  • static_cast
  • reinterpret_cast
  • dynamic_cast

Как уже упоминалось, первые три являются операциями времени компиляции. За их использование не взимается штраф во время выполнения. Это сообщения компилятору о том, что данные, которые были объявлены одним способом, должны быть доступны другим способом. «Я сказал, что это int*, но позвольте мне получить к нему доступ, как если бы это был char*, указывающий на sizeof(int) chars» или «Я сказал, что эти данные доступны только для чтения, и теперь мне нужно передать их функции, которая не будет их изменять. , но не принимает параметр как константную ссылку."

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

dynamic_cast — это конструкция времени выполнения, и она должна иметь стоимость времени выполнения.

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

person Max Lybbert    schedule 23.03.2009
comment
На самом деле, static_cast также может привести к операции во время выполнения, как и приведение в стиле c (например, когда вы выполняете приведение от числа с плавающей запятой к целому числу и наоборот). - person MikeMB; 22.04.2016
comment
Зачем использовать static_cast для преобразования между числами с плавающей запятой и целыми числами? Вы можете просто назначить: double d = 5; int i = d;. Преобразование может включать в себя стоимость времени выполнения, но вы должны заплатить эту стоимость, даже если вы не пишете static_cast. - person Max Lybbert; 22.04.2016
comment
Существуют контексты, в которых неявное сужающее преобразование не разрешено (например, инициализация списка) или когда вы хотите сделать преобразование явным. Например. выбрать определенную перегрузку или задокументировать (вам или компилятору), что вы знаете о происходящем преобразовании. Кроме того, преобразование float в int — это всего лишь один пример — подумайте об операторах/конструкторах явного преобразования классов или приведении указателя вверх/вниз в иерархии с множественным наследованием. - person MikeMB; 22.04.2016
comment
Да, это не дополнительные накладные расходы по сравнению с приведениями в стиле c или неявным преобразованием. Я хотел четко указать, что static_cast делает больше, чем просто говорит компилятору интерпретировать биты по-разному (что и делают reinterpret_cast и const_cast). - person MikeMB; 22.04.2016

При использовании dynamic_cast во время выполнения выполняется несколько проверок, чтобы вы не сделали глупостей (подробнее см. список рассылки GCC), стоимость одного dynamic_cast зависит от того, сколько классов затронуто, какие классы затронуты и т. д.
Если вы действительно уверены в безопасности приведения, вы все равно можете используйте reinterpret_cast.

person tstenner    schedule 23.03.2009
comment
Если вы обнаружите, что вам нужно использовать reinterpret_cast, вы определенно рискуете зайти на территорию, специфичную для реализации, и, возможно, на землю неопределенного поведения. - person ; 23.03.2009
comment
Обратите внимание, что reinterpret_cast может подчиняться другим правилам псевдонимов, чем static_cast и т. д. - person leander; 23.03.2009

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

Я видел несколько ошибок, зарегистрированных в моем текущем компиляторе, где генерация кода или оптимизация немного отличались в зависимости от того, используете ли вы приведение static_cast в стиле C или в стиле C++.

Так что если переживаете, проверьте разборку на хотспотах. В противном случае просто избегайте динамических приведений, когда они вам не нужны. (Если вы отключите RTTI, вы все равно не сможете использовать dynamic_cast.)

person leander    schedule 23.03.2009
comment
Мне было бы интересно увидеть пример использования dynamic_cast, когда он вам не нужен. - person ; 23.03.2009
comment
Например, если вы выполняете приведение к производному классу (поэтому вы часто используете dynamic_cast), но вы уже знаете, что приведение допустимо, поэтому вместо этого вы можете использовать static_cast. - person jalf; 23.03.2009
comment
@Neil: Думаю, я имел в виду больше в плане рефакторинга, чтобы избежать dynamic_cast. Я довольно внимательно смотрю на последствия, прежде чем начать вставлять виртуальные иерархии наследования, потому что я знаю, что иду на компромисс, и иногда выбор другого шаблона проектирования может решить проблему... - person leander; 24.03.2009
comment
@Neil: если вы уверены, что это безопасное приведение вниз и нет виртуального наследования, вы можете вместо этого использовать static_cast. Кроме того, я считаю, что вы можете использовать dynamic_cast для восходящих преобразований, но в целом это было бы глупо (возможно, за исключением виртуального наследования?). Вступая на менее известную территорию здесь... - person leander; 24.03.2009
comment
Я видел несколько ошибок, зарегистрированных в моем текущем компиляторе, где генерация кода или оптимизация немного отличались в зависимости от того, используете ли вы приведение static_cast в стиле C или в стиле C++.: Интересно, делает ли приведение в стиле C больше? чем что-то одно (const_cast и static_cast, например). - person Max Lybbert; 24.03.2009

Каноническая истина — это сборка, поэтому попробуйте обе и посмотрите, получится ли у вас другая логика.

Если вы получите точно такую ​​же сборку, то разницы никакой и быть не может. Единственное место, где вам действительно нужно придерживаться старых приведений C, — это подпрограммы и библиотеки чистого C, где нет смысла вводить зависимость от C++ только для приведения типов.

Одна вещь, о которой следует знать, это то, что приведения типов происходят повсюду в куске кода приличного размера. За всю свою карьеру я никогда не искал «все приведения» в части логики — вы, как правило, ищете приведения к определенному ТИПУ, например «A», а поиск по «(A)» обычно столь же эффективен, как и что-то вроде "static_cast‹A›". Используйте новые приведения типов для таких вещей, как проверка типов и тому подобное, не потому, что они облегчают поиск, который вы никогда не сделаете.

person user3726672    schedule 21.07.2014