вот почему нужно быть ближе к железу - ваш toolchain на грани нервного срыва из-за уровней абстракций. Люди которые были против перехода на Фортран (которых вы постоянно высмеиваете) были возможно правы - на том уровне железа и развития devops
Иногда мне кажется что они правы (эти голоса, что раздаются со всех сторон, когда иду по супермаркету) :)
Во времена моей юности знакомые проводили эксперименты по выявлению устойчивости софта к искажению машкода - буквально брали скомпиленный код на x86 и вносили в прогу случайные искажения в случайных местах и смотрели что случится, и выяснили, что сишный код (ТурбоСи, который), из всего доступного в тот момент оказался самым устойчивым - под пиво входили в раж и в порядке хохмы заменяли тело случайной функции на сплошные нули - Си это спокойно переваривал, даже большинство программ продолжали устойчиво работать и выдавать предсказуемый результат. У всяких разных ТурбоПаскалей крыша ехала намного быстрей, ну а байткоду просто сразу будет жопа.
Еще на эту тему вспоминается, что я как любитель железа очень любил почитывать про отказоустойчивые системы, и не то чтобы сильно удивился, но не совсем без удивления читал про то как сильно пришлось изменить подход к радиационностойким процессорам при переходе от CISC к RISC, потому что ежели в классических (несуперскалярных) CISC было достаточно для радиационностойкости защитить центральный пайплайн, который исполняет микропрограмму (а остальное там дубовое - что там например масочному ROM с микропрограммой сделается), то в RISC уже нужно было защищать намного больше, потому что состояние размазано по множеству узлов.
И вот видя как народ код пишет (в том числе и даже в особенности компиляторы), у меня временами буквально волосы на 5-й точке шевелятся, как подумаю, как этот ужас будет исполнять мой функциональный код..
Ну то есть я хочу сказать, что процедурный/объектный код просто работает на другом подмножестве состояний системы чем функциональный, и пока функциональный код встречается относительно редко, непонятные проблемы будут стабильно вылезать.
Ваш способ проверки устойчивости вызывает больше вопросов, чем предоставляет ответов.
В реальности вероятность сбоя маленькая, хотелось бы увидеть прикидку, по которой разница количества битов в разы меняет вероятность сбоя на проценты.
Зарисовывание функций нолями не означает устойчивость остальной программы к неверному результату функции. Если её забить нолями всю, т.е. включая ret, то в итоге выполняться будет следующая за ней функция в сегменте кода. А тут уже как карта ляжет - Сишная функция даст сбой в точке возврата, но в остальном останется рабочей. В Паскале функция даст не только сбой в точке возврата, но и стек подотрёт по-другому; а вот тут уже всякие кони будут проявляться и в функциях, вызванных позже.
Это совершенно правильный способ. В реальной жизни именно так и происходит, что сбоят с некоторой вероятностью отдельные ячейки памяти.
Вероятность что сбой подотрет именно ret очень низка.
Да, специфика подтирания стека в Паскале приводит к тому, что всего одна сбойная ячейка приводит к множественным сбоям в разных местах системы, то есть в Си сбой ячейки это не более чем сбой одной ячейки, а в Паскале сбой всего одной ячейки с ненулевой вероятностью порождает сразу целый каскад сбоев.
В "реальной жизни" считать такое поведение нормальным (или, тем более, превозносить) это или запредельная смелость, или откровенная глупость. С момента сбоя ячейки поведение не определено вообще. Я предпочту доверить свою жизнь херне, которая как можно быстрее упадёт и перезапустится, а не будет творить что-то непредсказуемое.
Сишный код использует ТАКОЕ подмножество кода процессора, что всё состояние системы даже не компактно расположено - его фактически просто нет в классическом Си. Виртмашины практически все активно используют сложные структуры данных, спагетти из кода с данными (виртмашину по-определению пишут непроцедурно), самомодифицирующийся код. - Если не делать самомодификацию кода, и держать все данные компактно в статическом месте, отдельно от исполняемого кода, и конечно не использовать хип (чисто стек), тогда байткод не будет по надежности отличаться от Сишного кода.
Ну, это байка для "настоящие программисты пишут на си". В реальности, существуют методы восстановления информации при потере её части. Вопрос только в их реализации на уровне транслятора.
Да ну, несерьзно это. Есть теория кодирования, её методы позволяют восстановить информацию в канале. Если вы о надежности систем (сбои памяти), то это, опять же, решается другими методами. Говорить о том, что программа на Си чем-то лучше других сродни утверждению что "Жигули"-копейка надежнее нового напичканного электроникой "Мерседеса" с автоматической коробкой, потому что у "жигуля" устройство проще. Как минимум, куча уязвимостией программ на Си связана с переполнением буфера, невозможном (теоретически) в той же Ява-машине.
Я не говорил что Си-программа лучше - она всего навсего ведет себя более предсказуемо. А что касается сравнения "к\опейки" с мерседесом, то вобщем в случае равного качества исполнения так оно и есть - более простые системы действительно надежнее. Естественно, в случае компьютера код сам по себе является системой, имеющей потенциальные проблемы, но в случае если надежность кода примерно одинакова, система на Си выиграет по надежности у системы на Java.
Всё очень серьезно. В разработке отказоустойчивых систем есть понятие "точка отказа", так вот обычно это выглядит, что есть некоторое количество ненадежных узлов, которые могут дублироваться/корректироваться, и есть сверхнадежные узлы, вероятность сбоя которых принципиально исчезающе мала, и вот эти сверхнадежные узлы и делают собственно коррекцию, и коммутируют ненадежные узлы (отключают отказавшие, подключают резервные).
Физически это выглядит так, что например в современном спутнике стоит несколько компьютеров (обычно от 2-х до 4-х), также продублирована часть других систем (двигатели, всяческая механика, обогреватели, обычно стоят парами или даже тройками, электропитание проведено 3+ проводами), и стоят совершенно дубовые коммутаторы, и стоит также совершенно дубовая, но очень надежная так-называемая "релейная" система управления (иногда и не одна), у которой отдельный радиоканал, и эта система позволяет управлять коммутаторами, а также подключена к аппаратным отладчикам всех компьютеров, и может каждый из этих компьютеров включить/выключить/резетнуть, а также как стандартный отладчик, остановить исполнение, запустить исполнение с другой точки, изменить значение каких-то ячеек памяти, итп. Плюс сам спутник делается таким образом, что в случае прекращения действия двигателей, он под действием космических факторов через некоторое время сам поворачивается к земле правильной стороной.
Например, в коде на классическом Си есть жесткое разделение исполняемого кода и данных, поэтому можно хранить код в отдельной памяти, и сделать сверхнадежными сам процессор, память стека, и устройство хранения кода, а память данных сделать ненадежной, и при каждом обращении к памяти данных, делать коррекцию ошибок. То же можно сделать и с системой на других языках, и с виртмашинами, но просто точек отказов станет намного больше.
Какое отношение имеет язык Си к тому, как менеджер памяти управляет её распределением? Или вы ограничиваетесь программами, оперирующими только статической памятью? Сегменты кода и данных существуют при любой компиляции в родной код. А в гарвардской архитектуре память программ и данных разделена явно. Опять же, гарвардская архитектура и язык Си - это малосвязанные вещи.
В разработке безопасных программ распределение кода и данных компилятором имеет далеко не первый приоритет. И язык Си здесь далеко не в лидерах.
> Или вы ограничиваетесь программами, оперирующими только статической памятью?
Не я, а конкретно данный случай.
> Сегменты кода и данных существуют при любой компиляции в родной код.
Нет. Это специфика процессоров с изначально ограниченной адресацией и затем расширенной, как x86 - нампример на m68k очень популярно кодировать всё подряд, в смысле лапшой, и использовать самомодифицирующийся код. Ну также сегментацией специфичны бизнес системы, тоже не всегда из безопасности, а часто от наследования старого кода.
> В разработке безопасных программ распределение кода и данных компилятором имеет далеко не первый приоритет.
Вы просто очень мало знаете про реальность разработки отказоустойчивых систем.
та в ваших схоластических кругах любой - лох, кто не из ваших схоластических кругов
а снаружи - ну секта и секта
сами себя замкнули в парадигме скрещивания ежей с ужами - а потом с героическим привлечением горних атсральных сущностей разруливаете проблемы с неизменно получающейся колючей проволокой
если еще короче: как-то выделять монады-хренады в инженерии не нужно монады побриты бритвой Оккама
какого нахуй мусора? IDE обновляется и после этого проект не собирается - ошибки в каких-то кишках. А кишки такие сложные потому что заточены на ConInt а она на TestDD
на скале, поди, и написано, с применением монад. вот и баги. время на дворе беспокойное, тревожное, приходится выбирать между чистотой монад и отсутствием жуков.
А вот интересно, что с монадами бывает в подобных случаях? В смысле, вызвали какую-то функцию с таким-то параметром, а она не знает, что с ним делать. Или, наоборот, вернула что-то, чего вызывающий не ожидает. Каков будет результат?
Рассказать по существу, что не получилось. В случае NPE - заворачивать в какой-нибудь тип; NPE вообще не должно существовать в природе.
Во втором случае само сообщение абсурдно. Хотя спасибо, что написали because. Но тут же мы как бы имеем дело с контролем версий - ну так и ведите себя реалистично; мерж, может быть, какой-нибудь. Глупо же удивляться неожиданностям и валить все это на юзера.
Ну NPE - это и есть "что-то не получилось". Рассказать по существу можно, если мы заранее позаботились проверить, получилось или нет. А если нет? Т.е., скажем, мы имеем метод, который, не знаю, нам число пи считает. И там внутри у него почему-то сломался думатель. И мы, допустим, по-умному сказали - пусть вернёт не Number, а Option какой-нибудь. И вот он вернул None. Мы в этом случае что будет сделать? Напишем - "не могу продолжать - мне не дают число пи, а дают None". А почему не дают? Неизвестно. Т.е. практически мы делаем немногим большее, чем делает бектрейс. А как сделать больше?
Т.е. либо надо каждую функцию оснастить специальной рассказывалкой (что в общем случае невозможно - есть же библиотеки), либо в конце концов будут всё равно случаи вида "что-то не так, а что - неизвестно, вот вам бектрейс".
None особой пользы не приносит в данном случае; она только показывать может, что это, давай ошибку обрабатывай. Лучше что-то более детальное, разумеется.
На практике это нередко "что-то не получилось, но забыли такой вариант обработать и пытаемся вызвать метод". Т.е. необязательно не получилось и ничего нельзя было поделать, может и можно было, но вот забыли. А Option просто не дает забыть обрабатывать такие случаи.
Ну как не даёт - вроде как Java не даёт не обрабатывать эксепшены, так тут же начинают писать catch(Exception e) { /* TODO */ } и так прямо в продакшен и пихают.
А, кстати, NPE относится к тем checked exceptions? Java разве заставляет все обращения к методам и полям в try-catch заворачивать на случай NPE?
То, что ленивый человек может замять обработку None явно (каким-то аналогом catch(Exception e) { /* TODO */ }), это конечно правда, но все ж это не то же самое, что просто забыть.
В общем да, с опшеном тa же фигня получается, если народ пишет option.get(). В этих всех скальных классах есть некоторое количество лишних функций, делающих фп несколько бессмысленным.
В высоконадежном программировании применяют расширенную типизацию и гард-выражения. Суть что в типе можно указать только те значения, для которых функция протестирована, соответственно виртмашина пристреливает процесс с ошибкой типа синего экрана, стектрейсом итп, естественно в некоторых пределах возможны всяческие try с обработкой ошибки. Гард-выражения в сущности делают то же, что расширенные типы, но чуть другой синтаксис и проверка находится на входе в функцию и позволяет применять бюилтин функции виртмашины. Пример - if (x%2 == 0)
Для невычислимого применяется аналитическое доказательство - ну например, что корень квадратный при вещественной арифметике, определен только для положительных чисел.
На практике, в реально критических приложениях (самолеты/ракеты/спутники) диапазон входных значений очень сильно ограничен физикой - например, типичный самолет на 10+км просто в принципе летит на скорости 800км/ч +-10км/ч - если больше может развалиться, если меньше - упадет, так что и смысла нет тестировать за пределами этого диапазона - важно только чтобы эту цифирь тщательно и безошибочно вписали в контракт (см "контрактное программирование"). - Например, самая дорогая в истории программная ошибка, как раз и случилась от того что на "Ариан-5" реиспользовали софт от "Ариан-4", а в контракте не учли что "Ариан-5" летает выше и быстрее чем "Ариан-4".
no subject
Date: 2014-07-16 05:07 am (UTC)Люди которые были против перехода на Фортран (которых вы постоянно высмеиваете) были возможно правы - на том уровне железа и развития devops
no subject
Date: 2014-07-16 05:10 am (UTC)Я в обоих случаях вижу лохов, не употребляющих монад.
no subject
Date: 2014-07-16 07:20 am (UTC)Во времена моей юности знакомые проводили эксперименты по выявлению устойчивости софта к искажению машкода - буквально брали скомпиленный код на x86 и вносили в прогу случайные искажения в случайных местах и смотрели что случится, и выяснили, что сишный код (ТурбоСи, который), из всего доступного в тот момент оказался самым устойчивым - под пиво входили в раж и в порядке хохмы заменяли тело случайной функции на сплошные нули - Си это спокойно переваривал, даже большинство программ продолжали устойчиво работать и выдавать предсказуемый результат.
У всяких разных ТурбоПаскалей крыша ехала намного быстрей, ну а байткоду просто сразу будет жопа.
Еще на эту тему вспоминается, что я как любитель железа очень любил почитывать про отказоустойчивые системы, и не то чтобы сильно удивился, но не совсем без удивления читал про то как сильно пришлось изменить подход к радиационностойким процессорам при переходе от CISC к RISC, потому что ежели в классических (несуперскалярных) CISC было достаточно для радиационностойкости защитить центральный пайплайн, который исполняет микропрограмму (а остальное там дубовое - что там например масочному ROM с микропрограммой сделается), то в RISC уже нужно было защищать намного больше, потому что состояние размазано по множеству узлов.
И вот видя как народ код пишет (в том числе и даже в особенности компиляторы), у меня временами буквально волосы на 5-й точке шевелятся, как подумаю, как этот ужас будет исполнять мой функциональный код..
Ну то есть я хочу сказать, что процедурный/объектный код просто работает на другом подмножестве состояний системы чем функциональный, и пока функциональный код встречается относительно редко, непонятные проблемы будут стабильно вылезать.
no subject
Date: 2014-07-16 09:05 am (UTC)no subject
Date: 2014-07-16 03:53 pm (UTC)no subject
Date: 2014-07-16 04:11 pm (UTC)В реальности вероятность сбоя маленькая, хотелось бы увидеть прикидку, по которой разница количества битов в разы меняет вероятность сбоя на проценты.
Зарисовывание функций нолями не означает устойчивость остальной программы к неверному результату функции. Если её забить нолями всю, т.е. включая ret, то в итоге выполняться будет следующая за ней функция в сегменте кода. А тут уже как карта ляжет - Сишная функция даст сбой в точке возврата, но в остальном останется рабочей. В Паскале функция даст не только сбой в точке возврата, но и стек подотрёт по-другому; а вот тут уже всякие кони будут проявляться и в функциях, вызванных позже.
no subject
Date: 2014-07-16 04:40 pm (UTC)Вероятность что сбой подотрет именно ret очень низка.
Да, специфика подтирания стека в Паскале приводит к тому, что всего одна сбойная ячейка приводит к множественным сбоям в разных местах системы, то есть в Си сбой ячейки это не более чем сбой одной ячейки, а в Паскале сбой всего одной ячейки с ненулевой вероятностью порождает сразу целый каскад сбоев.
no subject
Date: 2014-07-16 07:27 pm (UTC)no subject
Date: 2014-07-16 11:00 pm (UTC)no subject
Date: 2014-07-16 09:08 am (UTC)no subject
Date: 2014-07-16 04:02 pm (UTC)Виртмашины практически все активно используют сложные структуры данных, спагетти из кода с данными (виртмашину по-определению пишут непроцедурно), самомодифицирующийся код.
- Если не делать самомодификацию кода, и держать все данные компактно в статическом месте, отдельно от исполняемого кода, и конечно не использовать хип (чисто стек), тогда байткод не будет по надежности отличаться от Сишного кода.
no subject
Date: 2014-07-16 11:14 am (UTC)В реальности, существуют методы восстановления информации при потере её части.
Вопрос только в их реализации на уровне транслятора.
no subject
Date: 2014-07-16 03:54 pm (UTC)no subject
Date: 2014-07-16 05:58 pm (UTC)Если вы о надежности систем (сбои памяти), то это, опять же, решается другими методами.
Говорить о том, что программа на Си чем-то лучше других сродни утверждению что "Жигули"-копейка надежнее нового напичканного электроникой "Мерседеса" с автоматической коробкой, потому что у "жигуля" устройство проще.
Как минимум, куча уязвимостией программ на Си связана с переполнением буфера, невозможном (теоретически) в той же Ява-машине.
no subject
Date: 2014-07-16 11:07 pm (UTC)А что касается сравнения "к\опейки" с мерседесом, то вобщем в случае равного качества исполнения так оно и есть - более простые системы действительно надежнее.
Естественно, в случае компьютера код сам по себе является системой, имеющей потенциальные проблемы, но в случае если надежность кода примерно одинакова, система на Си выиграет по надежности у системы на Java.
no subject
Date: 2014-07-16 11:45 pm (UTC)Физически это выглядит так, что например в современном спутнике стоит несколько компьютеров (обычно от 2-х до 4-х), также продублирована часть других систем (двигатели, всяческая механика, обогреватели, обычно стоят парами или даже тройками, электропитание проведено 3+ проводами), и стоят совершенно дубовые коммутаторы, и стоит также совершенно дубовая, но очень надежная так-называемая "релейная" система управления (иногда и не одна), у которой отдельный радиоканал, и эта система позволяет управлять коммутаторами, а также подключена к аппаратным отладчикам всех компьютеров, и может каждый из этих компьютеров включить/выключить/резетнуть, а также как стандартный отладчик, остановить исполнение, запустить исполнение с другой точки, изменить значение каких-то ячеек памяти, итп.
Плюс сам спутник делается таким образом, что в случае прекращения действия двигателей, он под действием космических факторов через некоторое время сам поворачивается к земле правильной стороной.
Например, в коде на классическом Си есть жесткое разделение исполняемого кода и данных, поэтому можно хранить код в отдельной памяти, и сделать сверхнадежными сам процессор, память стека, и устройство хранения кода, а память данных сделать ненадежной, и при каждом обращении к памяти данных, делать коррекцию ошибок.
То же можно сделать и с системой на других языках, и с виртмашинами, но просто точек отказов станет намного больше.
no subject
Date: 2014-07-17 09:01 am (UTC)Сегменты кода и данных существуют при любой компиляции в родной код.
А в гарвардской архитектуре память программ и данных разделена явно.
Опять же, гарвардская архитектура и язык Си - это малосвязанные вещи.
В разработке безопасных программ распределение кода и данных компилятором имеет далеко не первый приоритет. И язык Си здесь далеко не в лидерах.
no subject
Date: 2014-07-17 01:43 pm (UTC)Не я, а конкретно данный случай.
> Сегменты кода и данных существуют при любой компиляции в родной код.
Нет. Это специфика процессоров с изначально ограниченной адресацией и затем расширенной, как x86 - нампример на m68k очень популярно кодировать всё подряд, в смысле лапшой, и использовать самомодифицирующийся код.
Ну также сегментацией специфичны бизнес системы, тоже не всегда из безопасности, а часто от наследования старого кода.
> В разработке безопасных программ распределение кода и данных компилятором имеет далеко не первый приоритет.
Вы просто очень мало знаете про реальность разработки отказоустойчивых систем.
no subject
Date: 2014-07-16 10:05 pm (UTC)а снаружи - ну секта и секта
сами себя замкнули в парадигме скрещивания ежей с ужами - а потом с героическим привлечением горних атсральных сущностей разруливаете проблемы с неизменно получающейся колючей проволокой
если еще короче: как-то выделять монады-хренады в инженерии не нужно
монады побриты бритвой Оккама
no subject
Date: 2014-07-16 09:18 am (UTC)no subject
Date: 2014-07-16 10:46 am (UTC)no subject
Date: 2014-07-16 04:04 pm (UTC)no subject
Date: 2014-07-17 06:47 am (UTC)no subject
Date: 2014-07-17 07:35 am (UTC)"If Java had true garbage collection, most programs would delete themselves upon execution." -- Robert Sewell
no subject
Date: 2014-07-16 06:48 am (UTC)no subject
Date: 2014-07-16 03:10 pm (UTC)no subject
Date: 2014-07-16 04:23 pm (UTC)no subject
Date: 2014-07-16 10:07 am (UTC)no subject
Date: 2014-07-16 03:11 pm (UTC)no subject
Date: 2014-07-16 02:42 pm (UTC)no subject
Date: 2014-07-16 03:11 pm (UTC)no subject
Date: 2014-07-16 09:09 pm (UTC)no subject
Date: 2014-07-16 09:49 pm (UTC)Во втором случае само сообщение абсурдно. Хотя спасибо, что написали because. Но тут же мы как бы имеем дело с контролем версий - ну так и ведите себя реалистично; мерж, может быть, какой-нибудь. Глупо же удивляться неожиданностям и валить все это на юзера.
no subject
Date: 2014-07-16 11:42 pm (UTC)Т.е. либо надо каждую функцию оснастить специальной рассказывалкой (что в общем случае невозможно - есть же библиотеки), либо в конце концов будут всё равно случаи вида "что-то не так, а что - неизвестно, вот вам бектрейс".
no subject
Date: 2014-07-17 12:23 am (UTC)no subject
Date: 2014-07-17 07:47 am (UTC)На практике это нередко "что-то не получилось, но забыли такой вариант обработать и пытаемся вызвать метод". Т.е. необязательно не получилось и ничего нельзя было поделать, может и можно было, но вот забыли. А Option просто не дает забыть обрабатывать такие случаи.
no subject
Date: 2014-07-17 08:06 am (UTC)no subject
Date: 2014-07-17 08:31 am (UTC)То, что ленивый человек может замять обработку None явно (каким-то аналогом catch(Exception e) { /* TODO */ }), это конечно правда, но все ж это не то же самое, что просто забыть.
no subject
Date: 2014-07-17 09:00 pm (UTC)no subject
Date: 2014-07-16 11:17 pm (UTC)Суть что в типе можно указать только те значения, для которых функция протестирована, соответственно виртмашина пристреливает процесс с ошибкой типа синего экрана, стектрейсом итп, естественно в некоторых пределах возможны всяческие try с обработкой ошибки.
Гард-выражения в сущности делают то же, что расширенные типы, но чуть другой синтаксис и проверка находится на входе в функцию и позволяет применять бюилтин функции виртмашины.
Пример - if (x%2 == 0)
no subject
Date: 2014-07-16 11:44 pm (UTC)А не упарятся на 2^64 значений тестировать? А если аргументов пять?
no subject
Date: 2014-07-17 12:57 am (UTC)no subject
Date: 2014-07-17 01:07 am (UTC)- Например, самая дорогая в истории программная ошибка, как раз и случилась от того что на "Ариан-5" реиспользовали софт от "Ариан-4", а в контракте не учли что "Ариан-5" летает выше и быстрее чем "Ариан-4".