Как исключение осуществляется под капотом?

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

Я ищу высококачественный материал. Я использую следующие языки: Java, C, С#, Python, С++, поэтому они представляют для меня наибольший интерес.

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

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

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

Спасибо.

Ответ 1

Исключения - это всего лишь конкретный пример более общего случая усовершенствованных нелокальных конструкций управления потоком. Другие примеры:

  • уведомления (обобщение исключений, первоначально из какой-то старой Lisp объектной системы, теперь реализованной, например, в CommonLisp и Ioke),
  • продолжения (более структурированная форма GOTO, популярная на языках высокого уровня, более высокого порядка),
  • сопрограммы (обобщение подпрограмм, особенно популярных в Lua),
  • генераторы à la Python (по существу ограниченная форма сопрограмм),
  • (совлокальные легкие нити) и, конечно, уже упомянутые
  • GOTO.

(Я уверен, что многие другие я пропустил.)

Интересным свойством этих конструкций является то, что они все примерно эквивалентны в выразительной силе: если он у вас есть, вы можете легко собрать все остальные.

Итак, как вы лучше всего реализуете исключения, зависит от того, какие другие конструкции у вас есть:

  • Каждый процессор имеет GOTO, поэтому вы всегда можете вернуться к нему, если нужно.
  • C имеет setjmp/longjmp, которые в основном являются продолжениями MacGyver (построенными из каналоволокна и зубочисток, не совсем реальными, но, по крайней мере, избавят вас от непосредственной проблемы, если у вас нет что-то более доступное).
  • JVM и CLI имеют свои собственные исключения, а это означает, что если семантика исключений вашего языка соответствует Java/С#, вы свободны на дому (но если нет, то вы ввернуты).
  • Parrot VM как исключения и продолжения.
  • У Windows есть своя структура для обработки исключений, которые разработчики языка могут использовать для создания собственных исключений сверху.

Очень интересный прецедент, как использование исключений, так и реализация исключений - это Microsoft Live Lab Volta Project. (В настоящее время не функционирует.) Цель Volta состояла в том, чтобы предоставить архитектурный рефакторинг для веб-приложений одним нажатием кнопки. Таким образом, вы можете превратить одноуровневое веб-приложение в двух- или трехуровневое приложение, просто добавив некоторые атрибуты [Browser] или [DB] в ваш код .NET, и тогда код будет автоматически запускаться на клиенте или в DB. Для этого код .NET должен был быть переведен на исходный код JavaScript, очевидно.

Теперь вы можете просто написать всю VM в JavaScript и запустить байт-код без изменений. (В принципе, перенос CLR с С++ на JavaScript.) Существуют проекты, которые делают это (например, HotRuby VM), но это неэффективно и не очень совместимо с другим кодом JavaScript.

Итак, вместо этого они написали компилятор, который компилирует байт-код CIL в исходный код JavaScript. Однако JavaScript не обладает определенными функциями, которые .NET(генераторы, потоки, а также две модели исключений не совместимы на 100%), и, что более важно, ему не хватает определенных функций, которые любят компиляторы (либо GOTO, либо продолжения), и это может использовать для реализации вышеупомянутых недостающих функций.

Однако JavaScript имеет исключения. Таким образом, они использовали исключения JavaScript для реализации Volta Continuations, а затем использовали Volta Continuations для реализации .NET Exceptions,.NET Generators и даже .NET Managed Threads (!!!)

Итак, чтобы ответить на ваш оригинальный вопрос:

Как исключения выполняются под капотом?

С Исключениями, по иронии судьбы! Во всяком случае, в этом очень конкретном случае.

Еще один замечательный пример - это некоторые из предложений по исключениям в списке рассылки Go, которые реализуют исключения с использованием Goroutines (что-то вроде смеси параллельных сопрограмм и процессов CSP). Еще одним примером является Haskell, который использует Monads, ленивую оценку, оптимизацию хвостового вызова и функции более высокого порядка для реализации исключений. Некоторые современные процессоры также поддерживают базовые строительные блоки для исключений (например, Vega-3 CPU, которые были специально разработаны для ускорителей Java Compute Acculerators).

Ответ 2

Вот общий способ реализации исключений С++:
http://www.codesourcery.com/public/cxx-abi/abi-eh.html

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

Вот хорошее описание того, как LLVM реализует исключения:
http://llvm.org/docs/ExceptionHandling.html

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

Ответ 3

В своей книге "Интерфейсы и реализации: методы создания многоразового программного обеспечения" Д. Р. Хансон обеспечивает хорошую реализацию исключений в чистом C, используя набор макросов и setjmp/longjmp. Он предоставляет макросы TRY/RAISE/EXCEPT/FINALLY, которые могут эмулировать почти все исключения С++ и многое другое.

Код можно просмотреть здесь (посмотрите на except.h/except.c).

P.S. ваш вопрос о Google. Их сотрудникам действительно разрешено использовать исключения в новом коде, а официальная причина запрета в старом коде - это потому, что он уже был написан таким образом, и не имеет смысла смешивать стили.

Лично я также считаю, что С++ без исключений - не лучшая идея.

Ответ 4

Компиляторы

C/С++ используют базовые возможности ОС для обработки исключений. Рамки, такие как .Net или Java, также полагаются на виртуальной машине в ОС. В Windows, например, реальный тяжелый подъем осуществляется SEH, инфраструктурой структурированного исключения. Вы должны абсолютно прочитать старую справочную статью: Курс Crash по глубинам обработки структурированных исключений Win32 ™.

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

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

Ответ 5

Лучшей бумагой, когда-либо написанной об осуществлении исключений (под капотом), является Обработка исключений в CLU Барбарой Лисковым и Аланом Снайдером. Я обращался к нему каждый раз, когда начинал новый компилятор.

Для более высокого уровня представления реализации на C с использованием setjmp и longjmp я рекомендую Dave Hanson C Интерфейсы и реализации (например, Эли Бендерски).

Ответ 6

setjmp() и longjmp() обычно.

У catching исключений нет нетривиальных затрат, но для большинства целей это не большая проблема.

Ответ 7

Ключевой задачей, которую нужно обработать исключением, является то, как вернуться к обработчику исключений после исключения исключения. Поскольку вы, возможно, произвели произвольное количество вложенных вызовов функций, так как оператор try в С++, он должен отключить стек вызовов, ищущий обработчик. Однако для реализации этой операции необходимо выполнить стоимость размера кода для поддержания достаточной информации для выполнения этой операции (и, как правило, это таблица данных для вызовов, которые могут принимать исключения). Это также означает, что путь динамического кода будет длиннее, чем просто возврат из вызовов функций (что довольно недорогая операция на большинстве платформ). В зависимости от реализации могут быть и другие затраты.

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

Приложением, в котором использование исключений (и С++ в целом) часто удается избежать по уважительным причинам, является встроенная прошивка. На типичных небольших платформах с металлическим или RTOS у вас может быть 1 МБ пространства кода, или 64 КБ, или даже меньше. Некоторые платформы настолько малы, что даже C нецелесообразно использовать. В такой среде влияние размера зависит от стоимости, упомянутой выше. Это также влияет на стандартную библиотеку. Встроенные поставщики инструментальных средств часто создают библиотеку без исключений, что оказывает огромное влияние на размер кода. Высоко оптимизирующие компиляторы могут также анализировать callgraph и оптимизировать необходимую информацию о кадре вызова для операции размотки для значительного сокращения пространства. Исключения также затрудняют анализ жестких требований в реальном времени.

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

Ответ 8

Код С++ в Google (за исключением некоторых случаев, относящихся к Windows) не использует исключения: cfr рекомендации, краткая форма: Мы не используем исключения С++ ". Цитата из обсуждения (нажмите стрелку, чтобы развернуть по URL-адресу):

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

Это правило не распространяется на код Google на других языках, таких как Java и Python.

Ответ 9

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

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

Ответ 10

Некоторые временные ряды, такие как время выполнения Objective-C, имеют 64-разрядные исключения с нулевой стоимостью. Это означает, что в блок try не входит ничего. Однако это дорого стоит, когда исключение выбрасывается. Это следует за парадигмой "оптимизировать для среднего случая" - исключения должны быть исключительными, поэтому лучше сделать так, чтобы исключений не было очень быстро, даже если это происходит за счет значительно более медленных исключений.