LINUX.ORG.RU

C++ exceptions: быть или не быть

 ,


5

7

Привет!
На работе спор плюсовиков: юзать или не юзать исключения от слова совсем.

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


Ох, борода у холивора уже километровая. Поиском воспользуйся

KennyMinigun ★★★★★
()

сгенерированных современными компиляторами

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

не юзать исключения от слова совсем

Не выйдет. Ибо std их использует, пусть и редко.

RazrFalcon ★★★★★
()

Исключения надо использовать там, где надо. И не использовать там, где не надо.[/thread]

anonymous
()

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

Те кто считал наносекунды, говорят, что если дописать к inline функции noexcept она становится бесплатной, хех.

Александреску тут недавно предложил вариант который устроит всех, даже любителей noexcept. Вот его доклад об этом на cpprussia.

pon4ik ★★★★★
()

«Technical Report on C++ performance, TR18015» от 2006-го года, раздел 5.4.

«Нет ничего проще, чем вызвать функцию, я сам это делал неоднократно»

«Are Exceptions in C++ really slow»

«C++ exception handling internals»

PS. Большинство сравнений производительности на тему exceptions vs error codes, которые доводилось видеть, представляли из себя микробенчмарки. Какие-то серьезные данные о том, как влияют на производительность исключения в больших кодовых базах, где куча бизнес-логики, практически и не попадались.

eao197 ★★★★★
()

1. Полностью отказаться от исключений не получится, так как на них завязана стандартная библиотека C++.

2. За перформансом в 99% случаев гоняются диванные теоретики. Но если очень хочется... В сравнении с if (...), когда исключения/ошибки не возникает, то код с исключениями быстрее. В случае возникновения исключения/ошибки, код с исключениями медленнее. Пруф. Далее можешь посчитать как часто у тебя будет код идти по exceptional ветке. И, да, как заметили выше, счет на наносекунды, именно поэтому я написал про теоретиков.

3. Я не видел пока программ без исключений, которые бы обрабатывали все возможные «исключительные» ситуации в вызываемых ими функциях. То есть код с исключениями безопасней, чем без них, при учете человеческого фактора. Disclamer: при нормальной реализации RAII.

4. Есть ситуации, когда без исключений никак или с жуткими костылями; например, конструкторы, перегруженные операторы и др.

То есть программировать на С++ без исключений как минимум глупо.

А чтобы от исключений не было мучительно больно, обязательно к просмотру Jon Kalb: Exception-Safe Coding in C++ part 1, part 2.

/thread

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)

насколько быстры сейчас исключения в бинарях, сгенерированных современными компиляторами

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

oldstable
()

Во-первых, есть разные анвайндеры. Во-вторых, есть mach-o, где apple смог сделать clang/llvm исключения на уровне gcc в Linux. Все зависит от того, как ты их будешь использовать. Можно бросать исключение и выходить из приложения - это норм, если хочешь восстанавливать состояние и при этом количество исключений бросаемых в приложении будет большое, то поиск fde во время анвайнда будет жрать столько ресурсов, что придется все переписать без исключений. Посмотри еще какие у вас библиотеки, может случиться так, что вся логика в них на исключениях.

xpahos ★★★★★
()

Избегайте цепепе. Возьмите лучше Go, будет меньше споров, больше дела и выхлопа и толку на работе.

azelipupenko
()
Ответ на: комментарий от Kroz

И, да, как заметили выше, счет на наносекунды, именно поэтому я написал про теоретиков.

Ты бы сначала посмотрел как устроено ABI, а потом бы говорил про наносекунды. Особенно интересно посмотреть на шареные библиотеки, т.к. eh frame не лежат в основном бинарнике и их нужно подгружать в какой-то кэш. Ну и в целом прокручивание стэка до нужного кэч блока весьма не тривиальная операция не стремящаяся к константному времени выполнения.

xpahos ★★★★★
()

Вот это в основном. Если он неправ, сообщите, потому что я полагаю, что прав.

Если исключение это «исключительная» и редкая ситуация, то нет причин их не использовать. Главное делать это правильно. Хотя я не стесняюсь запихивать логику в несколько веток исключений в питоне, но это другое.

anonymous
()
Ответ на: комментарий от azelipupenko

да да в гоу исключения неявным образом сразу вшиваются в бинарь

anonymous
()
Ответ на: комментарий от xpahos

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

anonymous
()

увольте их и наймите профи

anonymous
()
Ответ на: комментарий от anonymous

Они грузятся в момент вызова динамического линкера в определенный кэш, да. Но это все равно не сильно ускоряет процесс. На данный момент реализации на джампах на x86/amd64 отключены, вроде как на ARM все еще есть.

xpahos ★★★★★
()

Сейчас меня запинают тяжелыми шиповаными сапогами, но я не использую исключения. Просто если что-то не выполнилось, то всё что дальше по if уже не вызовется. Ну разве что в питоне иногда try юзаю...

I-Love-Microsoft ★★★★★
()
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)

Об этом спорили 10 лет назад, сейчас спорить может только необразованное ламерьё которое на знает языка. Сейчас исключения:

[*] Бесплатны по CPU, если они не кидаются
[*] Достаточно затратны по размеру бинарника
[*] Довольно дороги если кидаются
Подробности лучше всего посмотреть в докладах со всяких конференций. Очень крут вот этот: https://www.youtube.com/watch?v=XpRL7exdFL8

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

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

А, например, rust, ушёл не очень далеко - там ошибиться и забыть уже нельзя, но всё равно нужно руками писать unwrap'ы и аналогичную дребедень, которая имеет runtime оверхед

И только исключения позволяют писать happy path без единой лишней строки, а ошибку обработать только там где её можно обработать.

slovazap ★★★★★
()
Ответ на: комментарий от xpahos

Ты бы сначала посмотрел как устроено ABI, а потом бы говорил про наносекунды.

Хорошо: сколько? Когда это может стать bottleneck'ом?

Kroz ★★★★★
()

ну, если собираешься писать логику на исключениях, то ещё могут возникнуть такие сомнения... А так выше всё правильно написали :)

Sahas ★★★★☆
()
Ответ на: комментарий от I-Love-Microsoft

Сейчас меня запинают тяжелыми шиповаными сапогами, но я не использую исключения. Просто если что-то не выполнилось, то всё что дальше по if уже не вызовется.

А что ты делаешь, если исключение бросится из функции из стандартной библиотеки? Например, файл не смог открыться?

Kroz ★★★★★
()
Ответ на: комментарий от slovazap

но всё равно нужно руками писать unwrap'ы

Кому нужно? В 99% случаях это говнокод.

позволяют писать happy path без единой лишней строки
для написания _ безопасного кода

/0

catch (...) не от хорошей жизни.

RazrFalcon ★★★★★
()
Ответ на: комментарий от Kroz

Ничего не делаю. Пользуюсь адекватными фреймворками без экзепшенов. QFile если не открылся - скажет false. Если попытаться прочитать данные из неоткрытого файла - read даст ноль вот и всё. Вот как надо правильно жить :)

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от Kroz

Короче, exception для меня это ЛГБТ-хипсторский бесполезный модный механизм.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от RazrFalcon

catch (...) не от хорошей жизни.

Ну давайте я вам проиллюстрирую эту самую «не хорошую жизнь». 30KLOC кода, 28 (двадцать восемь) catch-ей. Из них всего 4 (четыре) catch(...). Из этих 4-х:

- один имеет вид catch(...) { std::terminate(); } и используется для того, чтобы эмулировать поведение noexcept на старых C++ компиляторах с неполной поддержкой C++11;

- один сохраняет std::current_exception() в экземпляр std::promise для того, чтобы тот, кто ждет на std::future получил исключение в своем потоке;

- два catch(...) написаны вокруг операций, которые имеют strong exception guarantee. Т.е. если они завершаются успешно, то все хорошо, если выскакивает исключение, то ничего не портится. Эти две операции предназначены для оптимизации хранения информации (т.е. при превышении некоторых порогов происходит попытка переместить данные из контейнера одного типа в другой). Если при выполнении такой операции возникает исключение, то оно игнорируется: ну не смогли оптимизировать сейчас хранилище, ну и фиг с ним, никакие данные не потерялись.

Так что, по сути, три catch(...) на 30K строк кода, в котором исключения активно применяются... Это говорит о том, что catch(...) в нормальных условиях практически не нужны.

Вот так дела на практике обстоят.

eao197 ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

я не использую исключения. Просто если что-то не выполнилось, то всё что дальше по if уже не вызовется.

Это ты так думаешь.

tailgunner ★★★★★
()
Ответ на: комментарий от RazrFalcon

Кому нужно? В 99% случаях это говнокод.

Ты о чём? Я о том что result надо явно раскрывать. unwrap, матчинг Ok/Err, ?, что угодно. Этого не должно быть в нормальном языке ни в каком виде, пока оно явно не понадобится.

/0

catch (...) не от хорошей жизни.

Давай ты будешь не случайные слова и символы писать, а попытаешься свою мысль донести если она у тебя, конечно, есть.

slovazap ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Подход Qt тоже спорный. Получается простой и понятный код, но надёжность страдает. Особенно учитывая то, что большинство функций возвращает тупо bool.

QFile если не открылся - скажет false.

А если нужно узнать какая конкретно была ошибка - нужно дёргать отдельный метод. При этом нельзя другие методы вызывать, ибо она перезапишется. Исключение было бы удобнее. А ещё лучше ADT.

RazrFalcon ★★★★★
()
Ответ на: комментарий от slovazap

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

это не Си говно, а у тебя руки из жопы. отсюда все проблемы. твои, а не языка программирования. а по бенчмаркам С самый быстрый и никакие проверки ему не вредят.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от slovazap

Я о том что result надо явно раскрывать. unwrap, матчинг Ok/Err, ?, что угодно.

unwarp и матчинг - абсолютно разные сущности.

Этого не должно быть в нормальном языке

Вкусовщина.

попытаешься свою мысль донести

Надёжность и исключения не совместимы. Так понятнее?

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Надёжность и исключения не совместимы.

Забыли дописать: «Доказано на примере катастрофы Ariane 5».

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Сферический код в вакууме?

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

Но если мне нужно выполнять кучу мелкий заданий, типа GUI, то нажатие каждой кнопки нужно оборачивать в catch(...), так как я не знаю какие исключения прилетят и прилетят ли вообще (да, я про бесполезный noexcept). При этом желательно показать пользователю сообщение сложнее «неизвестное исключение».

Про многопоточный код вообще молчу.

RazrFalcon ★★★★★
()
Ответ на: комментарий от slovazap

result надо явно раскрывать. unwrap, матчинг Ok/Err, ?, что угодно. Этого не должно быть в нормальном языке ни в каком виде

Должно. Потому что обработка ошибок - это неотъемлимая и важная часть программы. А исключения делают эту обработку в лучшем случае неуклюжей.

tailgunner ★★★★★
()
Ответ на: комментарий от Iron_Bug

да не, он сосёт у фортрана по бенчмаркам

anonymous
()
Ответ на: комментарий от Kroz

на них завязана стандартная библиотека C++

Есличо, из стандартной библиотеки исключения будут убирать. Если не все то большинство.

anonymous
()
Ответ на: комментарий от I-Love-Microsoft

https://herbsutter.com/2018/07/02/trip-report-summer-iso-c-standards-meeting-...

Gradually switching precondition violations from exceptions to contracts promises to eventually remove a majority of all exceptions thrown by the standard library(!). ... Being able to eliminate a majority of all exceptions, which eventually enables many more functions to be noexcept, is a huge improvement for both correctness and performance

anonymous
()
Ответ на: комментарий от RazrFalcon

unwarp и матчинг - абсолютно разные сущности.

Это не имеет никакого отношения к обсуждаемому.

Вкусовщина

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

Надёжность и исключения не совместимы. Так понятнее?

Голословное утверждение.

slovazap ★★★★★
()
Ответ на: комментарий от tailgunner

Должно. Потому что обработка ошибок - это неотъемлимая и важная часть программы.

Именно поэтому и не должно.

А исключения делают эту обработку в лучшем случае неуклюжей

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

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

С Result она размазана тонким слоем по всему коду, при чём даже не в виде полезного кода, а в виде мусора который просто прокидывает код ошибки выше.

Присутствие Result в сигнатуре, как минимум, сигнализирует, что функция не чистая. А паттерн матчинг позволяет свести проверки Result к по сути линейному коду, а не ветвлению.

Virtuos86 ★★★★★
()
Ответ на: комментарий от slovazap

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

Исключения позволяют писать, не обрабатывая ошибок вообще нигде (кроме catch(...) в main), чем люди и пользуются.

С Result она размазана тонким слоем по всему коду, при чём даже не в виде полезного кода

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

а в виде мусора который просто прокидывает код ошибки выше.

Этого мусора меньше, чем try-catch.

tailgunner ★★★★★
()
Ответ на: комментарий от Kroz

Хорошо: сколько?

Это сильно зависит от того сколько у тебя кэч блоков и какова глубина стэка, ресровятся ли дальше эксепшены итд. В целом от 250 мс для libgcc, от 300 мс для libunwind от hp и где-то от 700 мс для libunwind llvm.

Когда это может стать bottleneck'ом?

Когда у тебя много экспешенов?

xpahos ★★★★★
()
Ответ на: комментарий от slovazap

Это не имеет никакого отношения к обсуждаемому.

Имеет самое прямое.

лишние действия

Обработка ошибок не лишнее действие. ? никто не отменял.

Голословное утверждение.

Кто бы говорил.

RazrFalcon ★★★★★
()
Ответ на: комментарий от xpahos

В целом от 250 мс для libgcc, от 300 мс для libunwind от hp и где-то от 700 мс для libunwind llvm.

ШТА??!!!!11111 Миллисекунд? Ты издеваешься?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

ШТА??!!!!11111 Миллисекунд? Ты издеваешься?

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

xpahos ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.