Я смотрел систематическую обработку ошибок в C ++ - Андрей Александреску утверждает, что исключения в C ++ очень медленные.
Верно ли это для C ++ 98?
Я смотрел систематическую обработку ошибок в C ++ - Андрей Александреску утверждает, что исключения в C ++ очень медленные.
Верно ли это для C ++ 98?
Ответы:
Основная модель, используемая сегодня для исключений (Itanium ABI, VC ++ 64 бит), - это исключения модели с нулевой стоимостью.
Идея состоит в том, что вместо того, чтобы терять время, настраивая защиту и явно проверяя наличие исключений повсюду, компилятор генерирует дополнительную таблицу, которая отображает любую точку, которая может вызвать исключение (счетчик программ), в список обработчиков. Когда генерируется исключение, к этому списку обращаются для выбора правильного обработчика (если есть), и стек раскручивается.
По сравнению с типичной
if (error)
стратегией:if
когда возникает исключениеСтоимость, однако, измерить нетривиально:
dynamic_cast
тест для каждого обработчика)Таким образом, в основном промахи в кеше, и поэтому нетривиально по сравнению с чистым кодом ЦП.
Примечание: для получения дополнительной информации прочтите отчет TR18015, глава 5.4 Обработка исключений (pdf)
Итак, да, исключения выполняются медленно на исключительном пути , но в остальном они быстрее, чем явные проверки (
if
стратегия) в целом.Примечание: Андрей Александреску, кажется, сомневается в этом «побыстрее». Я лично видел, как все идет в обе стороны: некоторые программы работают быстрее с исключениями, а другие быстрее с ветвями, так что действительно, похоже, в определенных условиях наблюдается потеря оптимизируемости.
Это имеет значение ?
Я бы сказал, что это не так. Программа должна быть написана с учетом удобочитаемости , а не производительности (по крайней мере, не в качестве первого критерия). Исключения следует использовать, когда ожидается, что вызывающий не может или не захочет обработать сбой на месте и передать его вверх по стеку. Бонус: в C ++ 11 исключения можно упорядочивать между потоками с помощью стандартной библиотеки.
Это тонко, хотя я утверждаю, что он
map::find
не должен бросать, но я нормально отношусь кmap::find
возврату,checked_ptr
который выдает, если попытка разыменования не удалась, потому что он равен нулю: в последнем случае, как и в случае класса, который представил Александреску, вызывающий выбирает между явной проверкой и использованием исключений. Расширение прав и возможностей звонящего, не возлагая на него большую ответственность, обычно является признаком хорошего дизайна.источник
abort
позволит вам измерить двоичный размер и проверить, что время загрузки / i-cache ведет себя аналогичным образом. Конечно, лучше не попадать ни в одну изabort
...Когда вопрос был опубликован, я ехал к врачу в ожидании такси, так что у меня было время только на короткий комментарий. Но теперь, когда я прокомментировал и проголосовал за и против, я лучше добавлю свой собственный ответ. Даже если Матье уже ответил неплохо.
В C ++ исключения особенно медленны по сравнению с другими языками?
Повторная претензия
Если это буквально то, что утверждает Андрей, то на этот раз он очень вводит в заблуждение, если не совсем неправ. Ибо возникшие / выданные исключения всегда выполняются медленно по сравнению с другими базовыми операциями на языке, независимо от языка программирования . Не только в C ++ или в большей степени в C ++, чем в других языках, как указывает предполагаемое утверждение.
В общем, в основном независимо от языка, две основные языковые функции, которые на порядки медленнее остальных, потому что они переводятся в вызовы подпрограмм, которые обрабатывают сложные структуры данных, являются
выброс исключения и
распределение динамической памяти.
К счастью, в C ++ часто можно избежать того и другого в критичном ко времени коде.
К сожалению, нет такой вещи, как бесплатный обед , даже если эффективность C ++ по умолчанию довольно близка. :-) Эффективность, получаемая за счет исключения генерации исключений и распределения динамической памяти, обычно достигается за счет кодирования на более низком уровне абстракции с использованием C ++ как «лучшего C». А более низкая абстракция означает большую «сложность».
Большая сложность означает больше времени, затрачиваемого на обслуживание, и небольшую выгоду от повторного использования кода или ее отсутствие, что является реальными денежными затратами, даже если их сложно оценить или измерить. Т.е. с помощью C ++ можно при желании обменять некоторую эффективность программиста на эффективность выполнения. Решение о том, следует ли это делать, - это в значительной степени инженерное и интуитивное решение, потому что на практике можно легко оценить и измерить только прибыль, а не затраты.
Существуют ли какие-либо объективные показатели производительности исключения исключений C ++?
Да, международный комитет по стандартизации C ++ опубликовал Технический отчет о производительности C ++, TR18015 .
Что значит «медленные» исключения?
В основном это означает, что выполнение
throw
может занять очень много времени ™ по сравнению, например, сint
назначением, из-за поиска обработчика.Как TR18015 обсуждает в разделе 5.4 «Исключения», существуют две основные стратегии реализации обработки исключений:
подход, при котором каждый
try
-block динамически устанавливает перехват исключений, так что поиск по динамической цепочке обработчиков выполняется при возникновении исключения, иподход, при котором компилятор создает статические таблицы поиска, которые используются для определения обработчика возникшего исключения.
Первый очень гибкий и общий подход почти принудительно применяется в 32-битной Windows, тогда как в 64-битной среде и в * nix-land обычно используется второй, гораздо более эффективный подход.
Также, как обсуждается в этом отчете, для каждого подхода есть три основных области, в которых обработка исключений влияет на эффективность:
try
-блоки,обычные функции (возможности оптимизации), и
throw
-выражения.В основном, при использовании подхода с динамическим обработчиком (32-разрядная версия Windows) обработка исключений влияет на
try
блоки, в основном независимо от языка (потому что это вызвано схемой обработки структурированных исключений Windows ), в то время как подход со статической таблицей имеет примерно нулевую стоимость дляtry
- блоки. Обсуждение этого займет гораздо больше места и исследований, чем это практически возможно для ответа SO. Итак, подробности смотрите в отчете.К сожалению, отчет за 2006 год уже немного датирован концом 2012 года, и, насколько мне известно, нет ничего более нового сравнимого.
Еще одна важная точка зрения заключается в том, что влияние использования исключений на производительность сильно отличается от изолированной эффективности поддерживающих языковых функций, поскольку, как отмечается в отчете,
Например:
Затраты на обслуживание из-за разных стилей программирования (корректность)
Резервная
if
проверка отказов сайта вызова по сравнению с централизованнойtry
Проблемы с кешированием (например, более короткий код может поместиться в кеш)
В отчете есть другой список аспектов, которые необходимо учитывать, но в любом случае единственный практический способ получить достоверные факты об эффективности выполнения - это, вероятно, реализовать ту же программу с использованием исключения, а не с использованием исключений, в пределах установленного ограничения времени разработки и с разработчиками. знакомы с каждым способом, а затем ИЗМЕРИТЬ .
Как хорошо избежать накладных расходов, связанных с исключениями?
Правильность почти всегда важнее эффективности.
Без исключений легко может произойти следующее:
Некоторый код P предназначен для получения ресурса или вычисления некоторой информации.
Вызывающий код C должен был проверить на успех / неудачу, но этого не происходит.
Несуществующий ресурс или недопустимая информация используются в коде, следующем за C, что вызывает общий хаос.
Основная проблема - это пункт (2), где при обычной схеме кода возврата вызывающий код C не вынужден проверять.
Есть два основных подхода, которые заставляют такую проверку:
Где P напрямую выдает исключение в случае сбоя.
Где P возвращает объект, который C должен проверить перед использованием своего основного значения (в противном случае исключение или завершение).
Второй подход, AFAIK, впервые был описан Бартоном и Накманом в их книге « Научный и инженерный C ++: введение с расширенными методами и примерами» , где они представили класс, вызывающий
Fallow
«возможный» результат функции. Аналогичный классoptional
теперь предлагается библиотекой Boost. И вы можете легко реализоватьOptional
класс самостоятельно, используя вstd::vector
качестве носителя значения для случая результата, отличного от POD.При первом подходе вызывающий код C не имеет другого выбора, кроме как использовать методы обработки исключений. Однако при втором подходе вызывающий код C может сам решать, выполнять ли
if
проверку на основе или общую обработку исключений. Таким образом, второй подход поддерживает компромисс между программистом и эффективностью времени выполнения.Какое влияние оказывают различные стандарты C ++ на производительность исключений?
C ++ 98 был первым стандартом C ++. Для исключений он ввел стандартную иерархию классов исключений (к сожалению, весьма несовершенную). Основное влияние на производительность оказала возможность спецификаций исключений (удаленных в C ++ 11), которые, однако, никогда не были полностью реализованы основным компилятором Windows C ++ Visual C ++: Visual C ++ принимает синтаксис спецификации исключений C ++ 98, но просто игнорирует спецификации исключения.
C ++ 03 был просто техническим исправлением C ++ 98. Единственным действительно новым в C ++ 03 была инициализация значений . Что не имеет ничего общего с исключениями.
В стандарте C ++ 11 общие спецификации исключений были удалены и заменены
noexcept
ключевым словом.Стандарт C ++ 11 также добавил поддержку для хранения и повторного генерирования исключений, что отлично подходит для распространения исключений C ++ через обратные вызовы языка C. Эта поддержка эффективно ограничивает способ сохранения текущего исключения. Однако, насколько мне известно, это не влияет на производительность, за исключением той степени, в которой обработка исключений в новом коде может быть более легко использована с обеих сторон обратного вызова языка C.
источник
longjmp
обращаетесь к обработчику.try..finally
Конструкция может быть реализована без стека раскручивания. F #, C # и Java реализуютсяtry..finally
без использования раскрутки стека. Вы простоlongjmp
к обработчику (как я уже объяснил).Вы никогда не сможете утверждать о производительности, если не конвертируете код в сборку или не тестируете ее.
Вот что вы видите: (быстрая скамья)
Код ошибки не зависит от процента появления. Исключения имеют небольшие накладные расходы, если они никогда не генерируются. Как только вы их бросите, начнутся страдания. В этом примере он выбрасывается для 0%, 1%, 10%, 50% и 90% случаев. Когда исключения генерируются в 90% случаев, код в 8 раз медленнее, чем в случае, когда исключения генерируются в 10% случаев. Как видите, исключения очень медленные. Не используйте их, если их часто бросают. Если ваше приложение не требует работы в реальном времени, не стесняйтесь бросать их, если они возникают очень редко.
Вы видите много противоречивых мнений о них. Но, наконец, разве исключения бывают медленными? Я не сужу. Просто посмотрите тест.
источник
Это зависит от компилятора.
GCC, например, был известен очень низкой производительностью при обработке исключений, но за последние несколько лет ситуация стала значительно лучше.
Но обратите внимание, что обработка исключений должна - как следует из названия - быть исключением, а не правилом в вашей разработке программного обеспечения. Когда у вас есть приложение, которое выдает столько исключений в секунду, что влияет на производительность, и это по-прежнему считается нормальной работой, вам лучше подумать о том, чтобы действовать по-другому.
Исключения - отличный способ сделать код более читабельным, убрав весь этот неуклюжий код обработки ошибок, но как только они становятся частью обычного потока программы, за ними становится действительно трудно следить. Помните, что a
throw
в значительной степениgoto catch
замаскировано.источник
throw new Exception
- Java-ism. как правило, никогда не следует бросать указатели.Да, но это не важно. Зачем?
Прочтите это:
https://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx
По сути, это говорит о том, что использование исключений, подобных описанному Александреску (50-кратное замедление, потому что они используют
catch
aselse
), просто неправильно. Это сказано для тех, кто любит делать это так, как я хочу, чтобы С ++ 22 :) добавил что-то вроде:(обратите внимание, что это должен быть базовый язык, так как это в основном компилятор, генерирующий код из существующего)
result = attempt<lexical_cast<int>>("12345"); //lexical_cast is boost function, 'attempt' //... is the language construct that pretty much generates function from lexical_cast, generated function is the same as the original one except that fact that throws are replaced by return(and exception type that was in place of the return is placed in a result, but NO exception is thrown)... //... By default std::exception is replaced, ofc precise configuration is possible if (result) { int x = result.get(); // or result.result; } else { // even possible to see what is the exception that would have happened in original function switch (result.exception_type()) //... }
PS также обратите внимание, что даже если исключения настолько медленные ... это не проблема, если вы не проводите много времени в этой части кода во время выполнения ... Например, если деление с плавающей запятой выполняется медленно, и вы делаете его в 4 раза быстрее, что не имеет значения, если вы тратите 0,3% своего времени на деление FP ...
источник
Как и в Silico, сказано, что его реализация зависит от реализации, но в целом исключения считаются медленными для любой реализации и не должны использоваться в коде с высокой производительностью.
РЕДАКТИРОВАТЬ: Я не говорю, что не используйте их вообще, но для кода с высокой производительностью лучше их избегать.
источник