Комментарии 9
Мне кажется, это в первую очередь вопрос к создателям процессоров. Почему бы не иметь переполнение в качестве прерывания, аналогично делению на ноль? Приложение может управлять этим флагом, говоря о том, «игнорировать ли переполнение», а с компиляторов (точнее, с сгенерированных приложений) снимется огромная часть работы по определению этого на каждой операции посредством проверки флага.
+1
При переполнении процессор выставляет бит переноса в флаговом регистре. Приложение может проверить его, если оно опасается переполнения в этом конкретном месте.
Проблема с прерываниями состоит в том, что:
1) Приложение не может включать/выключать их, это может делать только ядро. Это непереносимо.
2) Прерывание опять же приходит в ядро, после чего ядро должно каким-то образом сообщить об этом приложению. Например, в случае линукса, если происходит деление на ноль, приложению прилетает SIGFPE, который сложно хендлить (попробуй узнай какой поток его вызвал). И тоже непереносимо между разными ОС.
В статье незря упоминалось, что иногда переполнение — это абсолютно нормально. Например, при рассчете CRC. Если бы у нас на каждую итерацию CRC/SHA1/etc прилетало прерывние, мы бы получали по два переключения контекста, что уронило бы производительность практически в ноль.
Проблема с прерываниями состоит в том, что:
1) Приложение не может включать/выключать их, это может делать только ядро. Это непереносимо.
2) Прерывание опять же приходит в ядро, после чего ядро должно каким-то образом сообщить об этом приложению. Например, в случае линукса, если происходит деление на ноль, приложению прилетает SIGFPE, который сложно хендлить (попробуй узнай какой поток его вызвал). И тоже непереносимо между разными ОС.
В статье незря упоминалось, что иногда переполнение — это абсолютно нормально. Например, при рассчете CRC. Если бы у нас на каждую итерацию CRC/SHA1/etc прилетало прерывние, мы бы получали по два переключения контекста, что уронило бы производительность практически в ноль.
+8
И ещё небольшое дополнение: прерывание при целочисельном делени на ноль возникается кажется только в x86. Например, arm просто записывает 0 в качестве результата и всё. Стандарт C говорит что результат деления на 0 будет undefined, если не ошибаюсь.
Это кажется довольно логичным, потому что процессор не должен заниматься валидацией обрабатываеммых данных. А исключение при делении на 0 — это рудимент эпохи программируемых калькуляторов.
Это кажется довольно логичным, потому что процессор не должен заниматься валидацией обрабатываеммых данных. А исключение при делении на 0 — это рудимент эпохи программируемых калькуляторов.
+3
в случае линукса, если происходит деление на ноль, приложению прилетает SIGFPE, который сложно хендлить (попробуй узнай какой поток его вызвал)
SIGFPE всегда приходит в тот же самый поток, который совершил деление на 0.
0
Да, действительно. Тут я ошибся. SIGFPE это thread-directed сигнал.
Но всё равно, сигнал либо придет асинхронно и тогда попробуй обработай его правильно. Исключение то нельзя бросить из обработчика. Только хардкор, только longjmp что подходит только для C и то с большими оговорками.
А если обратывать сигнал синхронно, то проще уж проверять делитель или флаговый регистр, так хоть переключений контекста не будет.
Но всё равно, сигнал либо придет асинхронно и тогда попробуй обработай его правильно. Исключение то нельзя бросить из обработчика. Только хардкор, только longjmp что подходит только для C и то с большими оговорками.
А если обратывать сигнал синхронно, то проще уж проверять делитель или флаговый регистр, так хоть переключений контекста не будет.
0
Вот только из ЯВУ получить доступ к флаговому регистру проблематично. Все сильно завязано на платформу.
86 содержит инструкции по арифметике с учетом этого флага и условный переход.
Это позволяет сложить 2 длинных числа в один цикл без проверок и нормализации.
Компилятор может выполнить такую оптимизацию, а может и не выполнить.
86 содержит инструкции по арифметике с учетом этого флага и условный переход.
Это позволяет сложить 2 длинных числа в один цикл без проверок и нормализации.
Компилятор может выполнить такую оптимизацию, а может и не выполнить.
0
Ну если мы пишем безопасный язык (например Rust), то мы можем заставить компилятор проверять флаговый регистр после каждой арифметической операции. Судя по приведенному ассемблерному коду, Rust так и делает.
А если мы пишем на чем-то типа С, то у нас есть целая куча либо быстрых, либо переносимых способов.
А если мы пишем на чем-то типа С, то у нас есть целая куча либо быстрых, либо переносимых способов.
0
Так-то Rust пытается быть и безопасным и быстрым. (:
+2
Ну так они предлагают разные средства. Хочешь быстро — используй wrapping_..., хочешь безопасно — используй checked_…
Кстати, новые gcc и clang поддерживают встроенные функции наподобие checked_. Правда, несовместимым между собой образом. Так что на C (в некоторых случаях) тоже можно складывать числа относительно быстро и безопасно.
Кстати, новые gcc и clang поддерживают встроенные функции наподобие checked_. Правда, несовместимым между собой образом. Так что на C (в некоторых случаях) тоже можно складывать числа относительно быстро и безопасно.
+1
Зарегистрируйтесь на Хабре , чтобы оставить комментарий
Мифы и легенды о переполнении целых чисел в Rust