Каким образом исключения C++ замедляют код, когда нет исключений thown?


Я читал, что есть некоторые накладные расходы на использование исключений C++ для обработки исключений, а не, скажем, проверки возвращаемых значений. Я говорю только о накладных расходах, которые возникают, когда не возникает никаких исключений. Я также предполагаю, что вам нужно будет реализовать код, который фактически проверяет возвращаемое значение и делает соответствующую вещь, что бы ни было эквивалентно тому, что сделал бы блок catch. И, это также нечестно сравнивать код, который бросает объекты исключения с 45 переменными состояния внутри кода, который возвращает отрицательное целое число для каждой ошибки.

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

6 51

6 ответов:

существует стоимость, связанная с обработкой исключений на некоторые платформы и с некоторые компиляторы.

а именно, Visual Studio при построении 32-разрядного целевого объекта будет регистрировать обработчик в каждой функции, имеющей локальные переменные с нетривиальным деструктором. В принципе, он устанавливает try/finally обработчик.

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

только try / catch и try / except block возьмите несколько инструкций для настройки. Накладные расходы, как правило, должны быть незначительными в каждом случае, за исключением самых жестких петель. Но вы обычно не используете try/catch/, кроме как во внутреннем цикле.

Я бы посоветовал не беспокоиться об этом, а вместо этого использовать профилировщик для оптимизации кода, где это необходимо.

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

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

здесь и некоторые накладные расходы с исключениями (как указывали другие ответы).

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

работают ли они с отключенными исключениями?

предположим, что они делают! Затем проверьте некоторые случаи, но обратите внимание, что вам нужно установить переключатель компиляции "отключить исключения". Без этого переключателя у вас все еще есть накладные расходы - даже если код никогда не создает исключений.

только накладные расходы ~6 инструкций, которые добавляют 2 SEH в начале функции и оставляют их в конце. Независимо от того, сколько попыток/уловов у вас есть в потоке, это всегда одно и то же.

и что это за локальные переменные? Я слышу, что люди всегда жалуются на них при использовании try/поймать. Я не понимаю, потому что деконструкторы в конечном итоге будут вызваны в любом случае. Также вы не должны позволять исключению подниматься более чем на 1-3 вызова.

Я взял тестовый код чипа Uni и немного расширил его. Я разделил код на два исходных файла (один с исключениями; один без). Я сделал каждый тестовый запуск 1000 раз, и я использовал clock_gettime() с CLOCK_REALTIME для записи времени начала и окончания каждой итерации. Затем я вычислил среднее значение и дисперсию данных. Я запустил этот тест с 64-разрядными версиями g++ 5.2.0 и clang++ 3.7.0 на коробке Intel Core i7 с 16 ГБ оперативной памяти, которая запускает ArchLinux с ядром 4.2.5-1-ARCH. Вы можете найти расширенный код и полные результаты здесь.

g++

ни одно исключение
  • среднее значение: 30,022,994 наносекунды
  • стандартное отклонение: 1.25327 e+06 наносекунд
Исключения
  • среднее значение: 30,025,642 наносекунды
  • стандартное отклонение: 1.83422 e+06 наносекунд

clang++

ни одно исключение
  • среднее значение: 20,954,657 наносекунд
  • Стандартное Отклонение: 426,662 наносекунды
Исключения
  • среднее значение: 23,916,638 наносекунд
  • стандартное отклонение: 1.72583 e+06 наносекунд

исключения C++ только наносят нетривиальный штраф за производительность с clang++, и даже этот штраф составляет всего ~14%.