Re[27]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.08.16 04:21
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, Sinclair, Вы писали:


S>>Эмм. Это будет иметь значение только в том случае, если мы рассматриваем инлайнинг f. А надо рассматривать инлайнинг foo — потому что после него код приобретает вид

S>>
S>>class ConcreteF
S>>{
S>>  int x;
S>>  int callee() { return x;}
S>>}
S>>var cf = new ConcreteF(0);
S>>return cf.callee();
S>>


EP>Не верно, так как непосредственно после инлайнинга получаем:

EP>
EP>class ConcreteF
EP>{
EP>  int x;
EP>  int callee() { return x;}
EP>}
EP>Func<...> cf = new ConcreteF(0);
EP>return cf.callee();
EP>

И в чём различие? В том, что номинативный тип cf — Func<int>, а фактический — ConcreteF?
Я же написал, что задача "вычислить точный тип в cf.callee()" решается одинаково хорошо независимо от языка. Тут даже спекулятивный инлайнинг не нужен, т.к. после тривиального преобразования (см. тж. SSA form) имеем:
class ConcreteF
{
  int x;
  int callee() { return x;}
}
return (new ConcreteF(0)).callee();

EP>Так вот я и говорю, что даже C++ компиляторы делают подобную оптимизацию только для простых случаев
Автор: Evgeny.Panasyuk
Дата: 26.02.15
, если же вставить аллокацию — то всё облом
Автор: Evgeny.Panasyuk
Дата: 24.02.15
. И это причём GCC, а не отстающий MS.

Имхо, тут дело не в аллокации, а тупо в том, что оптимизатор для лямбд недокручен. В примере компилятор заинлайнил и foo и bar. Но вот в bar он смог заинлайнить и дальше — надо полагать, по причине знания точного типа. А пот почему он не стал девиртуализовывать и инлайнить лямбду — хрен его знает. По-хорошему, надо проверять на интеловском компиляторе — они в области девиртуализации, как спекулятивной, так и консервативной, были во времена моей юности впереди планеты всей.
EP>1. Для этого есть Link-Time Code Generation / Link Time Optimization
Ну я про него слышал, но отношусь скептически. Как оно вообще работает? Неужто компилятор оставляет фрагменты синтаксических деревьев внутри бинарного кода, на случай если они потребуются линкеру?
То есть я могу поверить, что линкер способен вклеить бинарник на место вызова, но как он будет проводить дальнейшие оптимизации лично мне непонятно. А ведь инлайнинг сам по себе малополезен — стоимость вызова обычно пренебрежимо мала. Его ценность — в том, что он разблокирует широкий класс дополнительных оптимизаций. Ровно как в нашем примере — мы не можем оптимизировать код функции foo сам по себе, т.к. он слишком обобщённый.
Зато после встраивания foo в main у нас становится доступно много контекстной информации, и мы можем делать всякие умные вещи.
EP>2. Замыкания, ФВП и прочее в большинстве случаев именно в исходниках и поставляются.
Это не потому, что так удобнее, а потому, что другого выхода нет.
EP>Именно из ограничений языка и вытекают дополнительные сложности оптимизации.
Основная дополнительная сложность — ресурсные ограничения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[31]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 11.08.16 06:46
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

S>>Да и в C++ макросы оочень часто используются


EP>Во-первых не часто.

EP>Во-вторых изначально Страуструп пытался использовать макросы для обобщённого программирования — это очень неудобно, собственно поэтому мы и имеем шаблоны. Ты же предлагаешь воспроизводить грабли тридцатилетней давности посредством текстовых макросов
Я ничего такого не предлагаю. Я предлагаю использовать компиляцию в MSIL с опциями предполагающими специализацию дженериков и делегатов с инлайнингом методов.
Это можно добиться разными способами. Можно кодогенерацией, а можно и за счет преобразования MSIL кода.
и солнце б утром не вставало, когда бы не было меня
Re[2]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: ZloeBablo Германия  
Дата: 11.08.16 07:03
Оценка: +1
__>Как вы полагаете какие люди идут в си шарп? Правильно он сначала был велоинструктором 10 лет, потом программистом Шарепоинта на дельфи. Потом он еще 10 лет познавал гит (как некоторые эксперты си шарп на рсдн, до сих пор отсмеяться не могу).

тебе не кажется что в нетфликс набирают таких 10 человек. А микрософт сотнями... а то и тысячами... А так же надеюсь ты взял эти зарплаты из одного региона.
Re[31]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: · Великобритания  
Дата: 11.08.16 10:36
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>более того — для формально верифицированного кода они не нужны.

EP>·>Для формально верифицированного их разве кто-то пишет?
EP>Я имею в виду то, что после того как есть результат формальной верификации, можно их выключить и это никак не повлияют на корректность.
Если код формально верифицируется, то ассерты не нужны. Вообще. Смысл?

EP>·>Точнее не так. Если медленно, то можно перенести ассерт в юнит-тест.

EP>Sinix выше правильно заметил — assert'ы отрабатывают во время всех тестов, как юнит, так и интеграционных, и поэтому есть приличный шанс того что они поймают баг пропущенный непосредственно юнит-тестированием.
EP>Более того, юнит-тест конкретного компонента может успешно пройти при наличии бага, или например двойного бага — то есть false positive. При этом внутренний assert мог поймать эти баги на этом же прогоне. То есть assert'ы это отличное дополнение unit-тестов.
А конкретнее? Если ассерт можешь написать, почему это же нельзя выразить тестом?

EP>>>Точно также как и ценность других defensive примочек, а-ля bounds checking

EP>·>Не точно также же. В проде bounds checking валит исключения
EP>Да какая разница если у тебя в оттестированном и корректном коде не будет этих исключений?
Потенциально будет — если что-то неожиданно поломается, например, кто-то неверно использует компонент или не так "быстренько баг пофиксили".

EP>То есть ты определись — либо у тебя код оттестирован и defensive programming не нужен, либо не оттестирован и ты тестируешь его на своих пользователях

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

EP>>>Там некоторые из них опциональные, и не попадают в Release.

EP>·>Ух ты. Это как устроено? Что-то не могу разобраться...
EP>Conditional
EP>

EP>Conditional methods allow developers to create methods whose calls can be placed in the code and then either included or omitted during compilation based on a preprocessing symbol.

А оно правда надо? JIT прекрасно справляется и с тупым finalStaticBool=System.getProperty("DEBUG").....if(finalStaticBool) return.

EP>·>При релизном использовании отключены.

EP>Совсем необязательно — я их видел в коммерческом продукте имеющем сотни миллионов установок
Гы-гы. Ведь явно не от хорошей жизни, им тупо пришлось использовать ассерты не по назначению.

EP>>>В чём ты видишь отличие? Выход за пределы bounds это есть логическая ошибка, которой в корректном коде нет

EP>·>Эта ошибка явно прописана в спеке языка
EP>Да какая разница — это и есть логическая ошибка в твоём алгоритме.
Так она и сломает мой алгоритм, а не совершенно не относящийся к моему алгоритму код.

EP>>>·>По-моему излишнее усложнение. Собственно на основании чего пользователь будет принимать решение?

EP>>>1. На основании того какое быстродействие его устраивает
EP>·>Чем быстрее, тем лучше, очевидно.
EP>Совсем не очевидно, ибо это не единственный критерий.
Т.е. это вообще не критерий, на выбор-то не влияет.

EP>>>2. На основании того насколько корректен его код (здесь речь идёт про библиотеку и пользовательский код, ассерты внутри библиотеки могут ловить ошибки в пользовательском коде).

EP>·>Конечно, корректен, я что ламер некорректный код писать?
EP>Тогда тебе и не нужен никакой defensive programming
Т.е. предлагаешь какие-то субъективные оценки, гадание на кофейной гуще использовать для принятия решения какие ассерты включать, а какие нет?

EP>·>А ты как корректность своего кода оцениваешь?

EP>Зависит от конкретного случая — различные комбинации unit/integration тестов, разбиение на классы эквивалентности, поиск изоморфизмов, assert'ы на пред/пост-условия и ванрианты/инварианты, доказательства. Вот до чего пока не доходил, так это до выражения доказательств на формальных языках и их дальнейшей автоматизированной проверки.

EP>·>А вообще для этого сценария придумали логи и кастомизируемые (в т.ч. в рантайм) уровни логгирования. Ассерты тут пихать не надо.

EP>А причём тут логи? Если у тебя условие assert'а сфейлилось — то дальше всё может полететь в тартарары, и лучше всего пристрелить такую программу.
Так говорю же, использование ассертов не по назначению. Сфейлилось совсем — брось исключение, оно обработается как надо, а если сфейлилось, но не очень опасно, то пожалуйся в лог и продожи работу. Зачем куда-то лететь-то сразу?

В общем, подытожу. Ассерты нужны если:
1. Плохо дело с юнит-тестами. Ассерт куда угодно воткнуть легко и хоть какой-то эффект есть.
2. Плохо дело с логгированием. Нет нормального способа сообщать о каких-то непредвиденных ситуациях.
3. Плохо дело с обработкой ошибок. Проще грохнуть всё приложение, чем кинуть исключение или нормально обработать ошибочную ситуацию.

Т.е. получается что ассерт это такой индикатор code smells.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[28]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 11.08.16 11:08
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>И в чём различие? В том, что номинативный тип cf — Func<int>, а фактический — ConcreteF?


Да, в этом в том числе. Аллокация, динамический полиморфизм и необходимость более глубоких уровней инлайнинга.
Всё решаемо, но очевидно сложнее чем инлайнинг прямого вызова без аллокаций, причём на меньшей глубине.

И это только один из аспектов. Другой — вездесущие аллокации, которые даже если считать как zero cost, и не учитывать GC вовсе — всё равно являются индикатором увеличенной ветвистости по памяти, что само по себе является большим тормозом. Хорошо хоть структуры есть, но они ограниченные
Автор: Evgeny.Panasyuk
Дата: 11.10.14
, и не являются полноценной заменой классов.

S>Я же написал, что задача "вычислить точный тип в cf.callee()" решается одинаково хорошо независимо от языка.


Так дело в том что в C++ нет задачи вычислять фактический вызываемый метод — он известен по построению, ещё до любых оптимизаций.

EP>>Так вот я и говорю, что даже C++ компиляторы делают подобную оптимизацию только для простых случаев
Автор: Evgeny.Panasyuk
Дата: 26.02.15
, если же вставить аллокацию — то всё облом
Автор: Evgeny.Panasyuk
Дата: 24.02.15
. И это причём GCC, а не отстающий MS.

S>Имхо, тут дело не в аллокации, а тупо в том, что оптимизатор для лямбд недокручен.

Какой-то отдельный оптимизатор лямбд не нужен. Лямбда это обычная структура с невиртуальным методом operator()(...) внутри. Структуры такого типа повсеместно встречаются более 20 лет, и ничего особенного в них нет.
И в тех местах где нет динамического полиморфизма, оптимизаторы всех mainstream компиляторов прекрасно с ними справляются.

S>В примере компилятор заинлайнил и foo и bar. Но вот в bar он смог заинлайнить и дальше — надо полагать, по причине знания точного типа.


Да, там и тип известен, и аллокаций нет.

S>А пот почему он не стал девиртуализовывать и инлайнить лямбду — хрен его знает.


В примере по первой ссылке ситуация подобная — конкретный тип лямбды стирается, но при этом нет аллокаций — и в таком варианте оптимизаторы (и GCC, и Clang) генерируют код идентичный версии с конкретным типом.

S>По-хорошему, надо проверять на интеловском компиляторе — они в области девиртуализации, как спекулятивной, так и консервативной, были во времена моей юности впереди планеты всей.


Clang смог заинлайнить пример по второй ссылке (с аллокацией и косвенным вызовом), но как-то частично и не полностью (не смотря на SSA), то есть далеко от идеала.
Даже если какой-то из компиляторов (а-ля Intel) сможет сделать полную оптимизацию в худшем случае, то всё равно не опровергает мой тезис — оптимизировать такие конструкции сложнее чем обычные прямые вызовы без аллокаций. Рассмотренный пример — простейший, там даже лямбды не захватывают никакие переменные — по сути обычные функции.

EP>>1. Для этого есть Link-Time Code Generation / Link Time Optimization

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

Практически так — только встраивает не синтаксические деревья С++, а IL (тот же который используется при обычной оптимизации) в объектные файлы. И при линковке вызывается тот же бэкенд что и оптимизирует код в обычных случаях.

S>А ведь инлайнинг сам по себе малополезен — стоимость вызова обычно пренебрежимо мала. Его ценность — в том, что он разблокирует широкий класс дополнительных оптимизаций.


Да, естественно — часто это главное преимущество.

Тем не менее, стоимость вызова, а особенно нескольких вложенных, может существенно перевешивать стоимость выполняемой операции.
В обобщённом коде не редко встречается ситуация когда когда функция лишь является враппером над другой, и ничего по сути не делает — и так несколько уровней.
Например разнообразные обёртки над итераторами — поверх исходного итератора накручивается несколько уровней других (transform iterator, etc), при этом часть из операций просто forward'ятся, а конечная это например обычный инкремент указателя. В таких случаях стоимость вызовов (не столько безусловный переход, сколько передача параметров и дополнительный код) — может быть решающей.
Или например функции с переменным числом аргументов (через variadic templates) часто разруливаются рекурсивно — по сути добавляя лишний вызов на каждый аргумент.

EP>>2. Замыкания, ФВП и прочее в большинстве случаев именно в исходниках и поставляются.

S>Это не потому, что так удобнее, а потому, что другого выхода нет.

Да, экспорта шаблонов нету. Там где нужно скрыть исходники ФВП приходится стирать типы (например через std::function), либо поставлять только некоторые из воплощений шаблонов.
Но многие из используемых ФВП именно в исходниках и поставляются.
Re[31]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.08.16 11:31
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, gandjustas, Вы писали:


EP>>>И всё же ты не понимаешь. Класс да, генерируется, да размер на стороне вызова известен, но вот внутри дженерика размер стёрт, даже после всех подстановок, в C++ же его размер внутри шаблона известен во время компиляции.

G>>Какой размер внутри дженерика?
EP>Размер структуры/класса.
А он тут при чем?

G>>Кто стер?

EP>Программист, в том момент когда передал замыкание в функцию принимающую Func<...>. Для разных замыканий тип один и тот же — Func<...>
Ты хочешь сказать, что компилятор не знает как получить класс-замыкание функции-параметра? Это чушь полнейшая.


G>>В .NET вся информация о типах хранится, поэтому размер известен не только в compile-time, но и в run-time.

EP>Внутри дженерика эта информация во время компиляции неизвестна. Известно только то что есть в типах, а тип один для разных замыканий Func<...>
Все известно, но для программиста недоступно. Потому что не нужно.

EP>>>Перечитай десять раз, посмотри примеры выше, обрати внимание на static_assert — если не дойдёт, то задавай конкретный вопрос.

G>>Ты приведи пример на C#, который иллюстрирует то, что ты говоришь.

EP>Я приведу на C++, а ты попробуй привести аналог на C# (хотя бы псевдокод):...

Прости, но это говно. В реальности auto foo(F f) не дает никакой информации о том какой должен быть F. Скорее у тебя будет вместо F тип std::function и появится та же самая косвенность.
Только компилятор C++ через инлайнинг эту косвенность уберет, а C# — нет. Не занимается он этим.


G>>Потому что сейчас это звучит как бред сумасшедшего.

EP>Не хами. Если ты чего-то не понимаешь, то необязательно называть это бредом, это не конструктивно. Я всегда привожу необходимые примеры.
Ты пытаешься обилием "умных слов" скрыть свое поверхностное знание. Только ты не учел, что есть люди, которые "умные слова" понимают.

EP>>>Именно мешает. Я смотрел генерируемый код C++ компиляторами — простые виртуальные вызовы инлайнятся, если же вставить динамическую аллокацию (при прочих равных), то инлайна не происходит.

G>>Так то ограничение компилятора C++
EP>Так ты же говоришь что код C#/.NET будет транслироваться в C++ — я показываю что только этого недостаточно.
А по моим замерам — достаточно. То есть достаточно иметь компилятор, в который вложено на порядок больше трудозатрат, чем в компилятор C# и C# будет работать не хуже C++.

G>>принципиальных проблем инлайна с аллокациями нет.

EP>Принципиальных нет, я говорю о том что это сложнее.
Нет, ты говорил что аллокации мешают инлайну.

EP>Точно также как нет и принципиальных проблем генерировать супер-оптимальный код из Python.

А вот это неправда. В динамическом языке есть принципиальная проблема генерировать супер-оптимальный код. Некоторые паттерны использования могут обломать любую оптимизацию.

G>>Более сложная чем что? Какую еще часть твоей фразы надо прочитать, чтобы понять тайный смысл того, что ты пишешь?

EP>Выделил. Могу продублировать:
EP>

EP>>>>>>>Виртуальный вызов и прочее можно соптимизировать, но это очевидно более сложная задача чем заинлайнить простой вызов конкретного метода

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

EP>Если же повысить уровень абстракции — сделать сортировку произвольных элементов, произвольных последовательностей, произвольным предикатом — так как выглядят реальные сортировки а-ля std::sort — то результат C++ не изменится, а вот C# во много раз просядет. Собственно мой тест с JS ровно это и продемонстрировал, и там даже абстракций было меньше чем у сортировки.

Ты std::sort называешь высокоуровневым кодом? Прости, смешно.

В реальности такой подход не работает. Ты не сможешь написать эффективную сортировку, которая работает и для массива, и для односвязного списка. По факту у тебя будет несколько специализаций под разные типы контейнеров.
Re[29]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.08.16 11:51
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, gandjustas, Вы писали:


G>>>>Что такое "данимические замыкания" ? В IL замыкания превращаются просто в классы.

EP>>>При этом передаются через параметры, сохраняются в классы и т.п. посредством динамического полиморфизма, стирая исходный тип.
G>>Ты бредишь
EP>Хамство это конечно отличный, а главное конструктивный аргумент. Зачем думать, анализировать, спрашивать — когда можно просто нахамить — "не понимаю значит бред"
Ты пытаешься говорить умные слова, не понимая их смысл. Это и есть бред.

G>>В замыканиях нетполиморфизма

EP>Не в самих замыканиях. Напиши пример ФВП которая принимает простейшее замыкание.
Там тоже нет полиморфизма.

G>>>>Что такое "динамический IEnumerable"?

EP>>>Динамический полиморфизм для обхода последовательностей.
G>>Пример кода покажи, не ясно о чем ты пишешь.
EP>Вот.
И зачем его использовать в performance-critical коде?

G>>>>Это в смысле IEnumerable не по массивам? Зачем его использовать в performance-critical коде?

EP>>>Затем что последовательности могут быть выражены разными структурами данных.
G>>Не пойму о чем ты.

EP>std::vector, std::deque, std::string — это всё random access последовательности. На них можно писать алгоритмы на итераторах, без abstraction penalty.

Неправда. Эффективный sort для vector будет отличаться от эффективного sort для deque. Abstraction penality это не потому что компилятор не умеет девиртуализировать вызовы, а потому что детали абстрагируемого кода могут повлиять на быстродейсвтие.

EP>На C# IList даст приличную просадку. И это лишь один из примеров.

Просадку где?

EP>>>Собственно о том и речь — в C++ я могу накручивать уровни абстракций даже в performance-critical коде, без жёсткого penalty.

G>>Это только если инлайнинг сработает.
EP>И что характерно он работает, потому что язык к этому располагает. Потому что можно использовать ФВП, замыкания, и т.п. без всякого динамического полиморфизма.
Язык тут не при чем. 20 лет назад C++ был почти такой же, а инлайнинга почти не было. Странно, да?
Ты путеашь теплое с мягким. Быстродействие С++ — следствие миллинов трудочасов, вложенных в компилятор, а не свойство языка.
Например сейчас JS — достаточно быстрый язык. А 10 лет назад был одним из самых медленных. JS как язык за это время изменился? Практически нет. Зато гугл потратил огромную сумму денег на разработку v8 движка.

G>>>>А IEnumerable (foreach) с массивами действительно тормозит (накладные расходы на Enumerator оказываются больше, чем затратры на тело цикла). Как раз из-за недостатка инлайнинга, в C++ foreach нилайнится.

EP>>>Так дело не в самом foreach — ты если распишешь его вручную, то он также будет тормозить, за счёт динамического полиморфизма..
G>>Для обычных массивов jit оптимизирует foreach

EP>Вообще-то про foreach начал говорить ты, сначала сказал что тормозит, потом привёл пример где не тормозит. Круто чё.

Процитирую твою фразу:

Так дело не в самом foreach — ты если распишешь его вручную, то он также будет тормозить, за счёт динамического полиморфизма..

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

EP>Я же говорил про IEnumerable.

Ты хоть проверял какой код генерируется? Может там тоже нет виртуальных вызовов, девиртуализация срабатывает.

G>>C IEnumerable другая проблема, которая связана с ФВП. Вообще ФП в C# довольно медленно работает, не создавался язык под него.

EP>О чём и речь
Проблема в компиляторе, вернее в JIT, а не в самом языке.

G>>>>Или ты имеешь ввиду использование ФВП для обхода массивов? Оно действительно и в C++, и в C# плохо работает. И в C++ и в C# такой код вручную оптимизируется.

EP>>>ФВП в том числе. А с чего ты взял что они в C++ плохо работают? Наоборот, прекрасно работают и отлично оптимизируются.
EP>>>Пример C++ -> JS vs C# как раз ФВП и использует.
G>>За счет инлайнинга, который JIT не делает.
G>>Но ты же не будешь в performance-critical на автоматический инлайнинг полагаться.

EP>Так в том-то и фишка что буду, because I can! Зачем мне делать его руками, для всех используемых комбинаций алгоритмов * замыканий * последовательностей * прочих абстракций, когда с этим прекрасно справляется компилятор?

Многие как раз делают иначе, постностью отказываясь от фич C++. Тот же v8.
Они чего-то не знают?


G>>>>>>Машинный код генерируется из IL. В нем все указанные тобой оптимизации делаются элементарно.

G>>>>>>Но у JIT нет столько времени на пребразования, как у компилятора C++.
EP>>>>>Что мешает сразу генерировать оптимизированный IL?
G>>>>IL — высокоуровневый язык. Он и так достаточно оптимален на своем уровне.
EP>>>И что из этого? C# версия без ФВП тоже компилируется в тот же самый "высокоуровневый IL", но работает быстрее. Версия с ФВП могла бы давать точно такой же IL.
G>>Не могла бы, потому что делегаты.
EP>То есть таки язык мешает оптимизациям?
Нет, именно JIT. Это его задача определять, что нужно выкинуть делегат и заинлайнить вызов функции. Увы этого в JIT нету.

G>>>>Работает быстро за счет "hotspot". Большинство движков JS перекомпилируют код на основании профиля использования. Кроме того "работает быстро" — это когда не используются ФВП.

EP>>>Вот именно, в исходном коде на C++ были ФВП и замыкание, в результирующем JS они были выоптимизированы компилятором C++, так как это элементарная операция (никакого динамического полиморфизма) — именно поэтому и быстро.
G>>Ну так ты продолжаешь доказывать, что проблема в языке C#.
EP>Проблема в языке в том числе, за счёт повсеместного динамического полиморфизма и избыточных индерекций по памяти.
Да-да, повтори еще 5 раз.
Re[32]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 11.08.16 12:08
Оценка:
Здравствуйте, ·, Вы писали:

EP>>>>более того — для формально верифицированного кода они не нужны.

EP>>·>Для формально верифицированного их разве кто-то пишет?
EP>>Я имею в виду то, что после того как есть результат формальной верификации, можно их выключить и это никак не повлияют на корректность.
·>Если код формально верифицируется, то ассерты не нужны. Вообще. Смысл?

Был код с ассертами -> формально верифицировали его -> выключили ассерты. Это иллюстрация на тему:

EP>Отличительная черта assert'ов — в том что они могут быть опционально отключены, более того — для формально верифицированного кода они не нужны.


EP>>Более того, юнит-тест конкретного компонента может успешно пройти при наличии бага, или например двойного бага — то есть false positive. При этом внутренний assert мог поймать эти баги на этом же прогоне. То есть assert'ы это отличное дополнение unit-тестов.

·>А конкретнее? Если ассерт можешь написать, почему это же нельзя выразить тестом?

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

·>>>>>При хорошем покрытии тестами ценность ассертов падает до нуля.

EP>>>>Точно также как и ценность других defensive примочек, а-ля bounds checking
EP>>·>Не точно также же. В проде bounds checking валит исключения
EP>>Да какая разница если у тебя в оттестированном и корректном коде не будет этих исключений?
·>Потенциально будет — если что-то неожиданно поломается, например, кто-то неверно использует компонент или не так "быстренько баг пофиксили".

Так ты определись, либо у тебя "что-то неожиданно поломается", либо "При хорошем покрытии тестами ценность ассертов падает до нуля."

EP>>То есть ты определись — либо у тебя код оттестирован и defensive programming не нужен, либо не оттестирован и ты тестируешь его на своих пользователях

·>Оттестирован на требуемых спекой сценариях.

У тебя в "cпеке" описаны все сценарии, для всех компонент на всех уровнях?

EP>>Conditional

·>А оно правда надо? JIT прекрасно справляется и с тупым finalStaticBool=System.getProperty("DEBUG").....if(finalStaticBool) return.

Я не понимаю что ты хочешь сказать — то что в Java непосредственно этой фичи нет, но не всё так плохо и взамен будет работать какая-то другая техника?
Ну ОК, но как это к обсуждаемой теме относится? То что техника другая, это как-то отменяет факт опциональности ассертов?

EP>>·>При релизном использовании отключены.

EP>>Совсем необязательно — я их видел в коммерческом продукте имеющем сотни миллионов установок
·>Гы-гы. Ведь явно не от хорошей жизни, им тупо пришлось использовать ассерты не по назначению.

Без понятия — но проблемы в этом нет. Если нужен over-defenesive, то оставляй assert'ы и в release

EP>>>>В чём ты видишь отличие? Выход за пределы bounds это есть логическая ошибка, которой в корректном коде нет

EP>>·>Эта ошибка явно прописана в спеке языка
EP>>Да какая разница — это и есть логическая ошибка в твоём алгоритме.
·>Так она и сломает мой алгоритм, а не совершенно не относящийся к моему алгоритму код.

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

EP>>>>·>По-моему излишнее усложнение. Собственно на основании чего пользователь будет принимать решение?

EP>>>>1. На основании того какое быстродействие его устраивает
EP>>·>Чем быстрее, тем лучше, очевидно.
EP>>Совсем не очевидно, ибо это не единственный критерий.
·>Т.е. это вообще не критерий, на выбор-то не влияет.

Что? Как раз таки влияет

EP>>>>2. На основании того насколько корректен его код (здесь речь идёт про библиотеку и пользовательский код, ассерты внутри библиотеки могут ловить ошибки в пользовательском коде).

EP>>·>Конечно, корректен, я что ламер некорректный код писать?
EP>>Тогда тебе и не нужен никакой defensive programming
·>Т.е. предлагаешь какие-то субъективные оценки, гадание на кофейной гуще использовать для принятия решения какие ассерты включать, а какие нет?

Да, субъективные оценки основанные в том числе на объективных данных — тебя что удивляет?
Такие оценки повсеместно используются в софтостроении, и лишь мизерный процент кода формально верифицируется.
Вычисление любого нетривиального свойства кода крупного проекта является алгоритмически неразрешимой задачей

EP>>·>А вообще для этого сценария придумали логи и кастомизируемые (в т.ч. в рантайм) уровни логгирования. Ассерты тут пихать не надо.

EP>>А причём тут логи? Если у тебя условие assert'а сфейлилось — то дальше всё может полететь в тартарары, и лучше всего пристрелить такую программу.
·>Так говорю же, использование ассертов не по назначению. Сфейлилось совсем — брось исключение, оно обработается как надо, а если сфейлилось, но не очень опасно, то пожалуйся в лог и продожи работу. Зачем куда-то лететь-то сразу?

Так assert это и есть жёсткий фейл, семантически не менее (а зачастую более) жёсткий чем обычные исключения.

·>В общем, подытожу. Ассерты нужны если:

·>1. Плохо дело с юнит-тестами. Ассерт куда угодно воткнуть легко и хоть какой-то эффект есть.

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

·>2. Плохо дело с логгированием. Нет нормального способа сообщать о каких-то непредвиденных ситуациях.


Это ты не понимаешь семантику assert'а. Assert это очень жёсткий фейл.

·>3. Плохо дело с обработкой ошибок. Проще грохнуть всё приложение, чем кинуть исключение или нормально обработать ошибочную ситуацию.

·>Т.е. получается что ассерт это такой индикатор code smells.

Нет, и тут не верно. Это уже семантически не assert, так как assert опционален.
А то о чём ты говоришь повсеместно используется в C коде, действительно из-за кривой обработки ошибок, и что характерно не опционально.

Assert это не просто "ошибочная ситуация" а-ля неверный пользовательский ввод и т.п., это БАГ в коде.
Re[3]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: a_g_99 США http://www.hooli.xyz/
Дата: 11.08.16 12:17
Оценка:
Здравствуйте, ZloeBablo, Вы писали:

ZB>тебе не кажется что в нетфликс набирают таких 10 человек. А микрософт сотнями... а то и тысячами... А так же надеюсь ты взял эти зарплаты из одного региона.


Тысячами. Но и гугл набирает тысячу в год а зарплаты у них по факту в 4 раза выше считая бонусы. И факт остается фактом — на данный момент си_шарп один из наиболее низкооплачиваемых и менеее востребованных скиллов в США.
А ответ почему в принципе очевиден
Re[32]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 11.08.16 12:34
Оценка:
Здравствуйте, gandjustas, Вы писали:

EP>>>>И всё же ты не понимаешь. Класс да, генерируется, да размер на стороне вызова известен, но вот внутри дженерика размер стёрт, даже после всех подстановок, в C++ же его размер внутри шаблона известен во время компиляции.

G>>>Какой размер внутри дженерика?
EP>>Размер структуры/класса.
G>А он тут при чем?

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

G>>>Кто стер?

EP>>Программист, в том момент когда передал замыкание в функцию принимающую Func<...>. Для разных замыканий тип один и тот же — Func<...>
G>Ты хочешь сказать, что компилятор не знает как получить класс-замыкание функции-параметра? Это чушь полнейшая.

Это передёргивание. Я говорю что это сложнее, а не невозможно, и примеры выше это подтверждают.

EP>>>>Перечитай десять раз, посмотри примеры выше, обрати внимание на static_assert — если не дойдёт, то задавай конкретный вопрос.

G>>>Ты приведи пример на C#, который иллюстрирует то, что ты говоришь.
EP>>Я приведу на C++, а ты попробуй привести аналог на C# (хотя бы псевдокод):...
G>Прости, но это говно.

Говно это твоё поведение. Ты неадекватен, не умеешь нормально вести диалог без всяких фекалий.

G>В реальности auto foo(F f) не дает никакой информации о том какой должен быть F. Скорее у тебя будет вместо F тип std::function и появится та же самая косвенность.


Не надо фантазировать. В подавляющем большинстве кода с ФВП на C++ — никаких std::function.

G>Только компилятор C++ через инлайнинг эту косвенность уберет, а C# — нет. Не занимается он этим.


Сейчас не убирает — пример выше — так это сложнее

EP>>>>Именно мешает. Я смотрел генерируемый код C++ компиляторами — простые виртуальные вызовы инлайнятся, если же вставить динамическую аллокацию (при прочих равных), то инлайна не происходит.

G>>>Так то ограничение компилятора C++
EP>>Так ты же говоришь что код C#/.NET будет транслироваться в C++ — я показываю что только этого недостаточно.
G>А по моим замерам — достаточно.

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

G>>>принципиальных проблем инлайна с аллокациями нет.

EP>>Принципиальных нет, я говорю о том что это сложнее.
G>Нет, ты говорил что аллокации мешают инлайну.

И до сих пор говорю — мешают, это труднее — и что характерно выше есть пример именно на эту тему.

EP>>Точно также как нет и принципиальных проблем генерировать супер-оптимальный код из Python.

G>А вот это неправда. В динамическом языке есть принципиальная проблема генерировать супер-оптимальный код. Некоторые паттерны использования могут обломать любую оптимизацию.

Нет принципиальной проблемы использовать всю доступную информацию для генерации супер-оптимального кода. Если же какая-то информация не доступна на момент компиляции (например текст eval приходит по сети) — то в аналогичном случае и языки со статической типизацией ничем не помогут.

G>Оптимизировать вирутальные вызовы несложно. Но без мощного инлайна это бывает невозможно.


Это сложнее. Выше как раз пример на компиляторе с мощным инлайном.

EP>>Если же повысить уровень абстракции — сделать сортировку произвольных элементов, произвольных последовательностей, произвольным предикатом — так как выглядят реальные сортировки а-ля std::sort — то результат C++ не изменится, а вот C# во много раз просядет. Собственно мой тест с JS ровно это и продемонстрировал, и там даже абстракций было меньше чем у сортировки.

G>Ты std::sort называешь высокоуровневым кодом? Прости, смешно.

У него уровень абстракции намного выше чем в твоём тесте сортирующем конкретные int'ы, конкретным предикатом, в конкретном контейнере.

G>В реальности такой подход не работает. Ты не сможешь написать эффективную сортировку, которая работает и для массива, и для односвязного списка. По факту у тебя будет несколько специализаций под разные типы контейнеров.


Можно иметь по одной сортировке для каждой концепции — всех вариантов списков, для всех видов массивов и т.п. Если же брать алгоритмы типа std::find_if — то они будут одинаково хорошо работать и для списков, и для массивов и т.п.

Но даже не вдаваясь во все эти подробности — попробуй хотя бы предикат и тип элемента абстрагировать — сразу получишь penalty.
Re[4]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: ZloeBablo Германия  
Дата: 11.08.16 12:52
Оценка:
А можно пример какой то что зп в 4 раза выше в одном и том же регионе? А то я тоже могу показать в мюнхене и дрездене.
Re[31]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: chaotic-kotik  
Дата: 11.08.16 13:06
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Да, это преимущество. Но с другой стороны в юнит тестами намного проще перевести код в состояние всех edge-case'ов (в которых в том числе могут выстрелить assert'ы) по сравнению с интеграционными тестами.


В общем случае это недостижимо, так как количество edge-case-ов растет экспоненциально, по отношению к количеству ветвлений в программе. Тот кто занимался fuzz-тестированием меня поймет. Язык тут роли не играет и вообще ничего не решает.
Re[5]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: a_g_99 США http://www.hooli.xyz/
Дата: 11.08.16 13:09
Оценка:
Здравствуйте, ZloeBablo, Вы писали:

ZB>А можно пример какой то что зп в 4 раза выше в одном и том же регионе? А то я тоже могу показать в мюнхене и дрездене.


MS vs Amazon в Сиэттле — у последнего базовая ставка выше в 2.5 раза (про бонусы не знаю)
MS vs Fb в Сиэттле — разница в 3-4 раза в зависимости от позиции
Re[30]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 11.08.16 13:29
Оценка: -1 :)
Здравствуйте, gandjustas, Вы писали:

EP>>Хамство это конечно отличный, а главное конструктивный аргумент. Зачем думать, анализировать, спрашивать — когда можно просто нахамить — "не понимаю значит бред"

G>Ты пытаешься говорить умные слова, не понимая их смысл. Это и есть бред.

Если тебе что-то недоступно — ты не стесняйся, спрашивай, без проблем объясню.

G>>>В замыканиях нетполиморфизма

EP>>Не в самих замыканиях. Напиши пример ФВП которая принимает простейшее замыкание.
G>Там тоже нет полиморфизма.

Ты код напиши — это несколько строк — тогда разговор будет предметным. Второе сообщение уже увиливаешь

G>>>>>Что такое "динамический IEnumerable"?

EP>>>>Динамический полиморфизм для обхода последовательностей.
G>>>Пример кода покажи, не ясно о чем ты пишешь.
EP>>Вот.
G>И зачем его использовать в performance-critical коде?

О том и речь — он тормозит. А вот диапазоны C++ — нет

EP>>std::vector, std::deque, std::string — это всё random access последовательности. На них можно писать алгоритмы на итераторах, без abstraction penalty.

G>Неправда. Эффективный sort для vector будет отличаться от эффективного sort для deque.

В случае deque — да — для неё может быть более оптимальный алгоритм, но этот будет работать в том числе. ОК, допустим вычеркнем deque — всё равно исключительно в поставке C++ имеем встроенные массивы, std::vector, std::string, std::array.

G>Abstraction penality это не потому что компилятор не умеет девиртуализировать вызовы,


По этому в том числе.

G>а потому что детали абстрагируемого кода могут повлиять на быстродейсвтие.


Могут влиять, а могут и не влиять std::find_if работает одинаково хорошо как для массивов, так и для списков.

EP>>На C# IList даст приличную просадку. И это лишь один из примеров.

G>Просадку где?

В коде использующем его вместо конкретных контейнеров.

EP>>>>Собственно о том и речь — в C++ я могу накручивать уровни абстракций даже в performance-critical коде, без жёсткого penalty.

G>>>Это только если инлайнинг сработает.
EP>>И что характерно он работает, потому что язык к этому располагает. Потому что можно использовать ФВП, замыкания, и т.п. без всякого динамического полиморфизма.
G>Язык тут не при чем. 20 лет назад C++ был почти такой же, а инлайнинга почти не было. Странно, да?

Был, менее сильный чем сейчас, но тем не менее был: http://www.osl.iu.edu/download/research/mtl/papers/iscope_final.pdf

G>Ты путеашь теплое с мягким. Быстродействие С++ — следствие миллинов трудочасов, вложенных в компилятор, а не свойство языка.


Не путаю, C++ быстрый в том числе потому что его легко оптимизировать — очевидно что встраивание обычного вызова легче чем виртуального.

EP>>Я же говорил про IEnumerable.

G>Ты хоть проверял какой код генерируется? Может там тоже нет виртуальных вызовов, девиртуализация срабатывает.

Проверял, но в другом случае
Автор: Evgeny.Panasyuk
Дата: 20.06.15
.
Если же брать этот конкретный случай — то он тормозит восьмикратно по сравнению с ручной подстановкой конкретных типов.

G>>>C IEnumerable другая проблема, которая связана с ФВП. Вообще ФП в C# довольно медленно работает, не создавался язык под него.

EP>>О чём и речь
G>Проблема в компиляторе, вернее в JIT, а не в самом языке.

В языке в том числе.

EP>>Так в том-то и фишка что буду, because I can! Зачем мне делать его руками, для всех используемых комбинаций алгоритмов * замыканий * последовательностей * прочих абстракций, когда с этим прекрасно справляется компилятор?

G>Многие как раз делают иначе, постностью отказываясь от фич C++. Тот же v8.
G>Они чего-то не знают?

Я не знаю что там конкретно внутри v8, и на основе чего они принимали какие-то там решения.
Но есть реальные и публичные примеры, где абстракции используются в полный рост, при этом давая исключительную производительность.
Re[33]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: · Великобритания  
Дата: 11.08.16 13:46
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>>>более того — для формально верифицированного кода они не нужны.

EP>>>·>Для формально верифицированного их разве кто-то пишет?
EP>>>Я имею в виду то, что после того как есть результат формальной верификации, можно их выключить и это никак не повлияют на корректность.
EP>·>Если код формально верифицируется, то ассерты не нужны. Вообще. Смысл?
EP>Был код с ассертами -> формально верифицировали его -> выключили ассерты. Это иллюстрация на тему:
Если код формально верифицирован — ассерты можно тупо удалить. Формальная верификация — сильная, но дорогая техника доказательства корректности кода, которая полностью заменяет ЮТ и ассерты.

EP>>>Более того, юнит-тест конкретного компонента может успешно пройти при наличии бага, или например двойного бага — то есть false positive. При этом внутренний assert мог поймать эти баги на этом же прогоне. То есть assert'ы это отличное дополнение unit-тестов.

EP>·>А конкретнее? Если ассерт можешь написать, почему это же нельзя выразить тестом?
EP>Например двойная ошибка в коде. Из-за первой ошибки перешёл в неправильное состояние, из-за второй перешёл из неправильного в состояние подобное правильному. И вроде бы тест случай проверил, но ошибку не выявил.
EP>При этом assert может элементарно проверять состояние между ошибками. А вот соответствующий юнит-тест мог бы быть на порядки менее очевидным, если вообще возможным.
Если ты вдруг полагаешь, что где-то может быть ошибочное состояние (т.е. хочешь написать там ассерт), то
либо воспроизведи это состояние тестом, т.е. ассерт не нужен;
либо почеши голову и убедись, что это состояние невозможно, т.е. ассерт не нужен.

EP>>>То есть ты определись — либо у тебя код оттестирован и defensive programming не нужен, либо не оттестирован и ты тестируешь его на своих пользователях

EP>·>Оттестирован на требуемых спекой сценариях.
EP>У тебя в "cпеке" описаны все сценарии, для всех компонент на всех уровнях?
Зачем на всех уровнях? Ошибка на уровне юнитов будет падать сразу с каким-нибудь bounds checking и видна немедленно и везде. В отличие от ассертов, которые в отключенном состоянии вызовут неопределённое поведение.
А высокоуровневые сценарии должны проходить, да.

EP>>>·>При релизном использовании отключены.

EP>>>Совсем необязательно — я их видел в коммерческом продукте имеющем сотни миллионов установок
EP>·>Гы-гы. Ведь явно не от хорошей жизни, им тупо пришлось использовать ассерты не по назначению.
EP>Без понятия — но проблемы в этом нет. Если нужен over-defenesive, то оставляй assert'ы и в release
Это не имеет смысла, используют инструмент не по назначению. Это уже тогда не ассерты, а обычные проверки.

EP>>>·>Эта ошибка явно прописана в спеке языка

EP>>>Да какая разница — это и есть логическая ошибка в твоём алгоритме.
EP>·>Так она и сломает мой алгоритм, а не совершенно не относящийся к моему алгоритму код.
EP>Программа не выполнила свою задачу, не пришла в обещанный postcondition, не зависимо от того что где поломалось.
Собственно в этом и проблема, что ты говоришь "программа", т.е. весь процесс. Хотя ошибка только в моём алгоритме. Плохо, когда локальная проблема внезапно становится глобальной. Именно за это не любят глобальные переменные, именно поэтому изобрели всякие защищённые режимы и т.п.

EP>>>·>Конечно, корректен, я что ламер некорректный код писать?

EP>>>Тогда тебе и не нужен никакой defensive programming
EP>·>Т.е. предлагаешь какие-то субъективные оценки, гадание на кофейной гуще использовать для принятия решения какие ассерты включать, а какие нет?
EP>Да, субъективные оценки основанные в том числе на объективных данных — тебя что удивляет?
EP>Такие оценки повсеместно используются в софтостроении, и лишь мизерный процент кода формально верифицируется.
EP>Вычисление любого нетривиального свойства кода крупного проекта является алгоритмически неразрешимой задачей
Т.е. придумали себе сложность и героически её решаем.

EP>·>Так говорю же, использование ассертов не по назначению. Сфейлилось совсем — брось исключение, оно обработается как надо, а если сфейлилось, но не очень опасно, то пожалуйся в лог и продожи работу. Зачем куда-то лететь-то сразу?

EP>Так assert это и есть жёсткий фейл, семантически не менее (а зачастую более) жёсткий чем обычные исключения.
"Опциональный жесткий фейл". Это не звучит для тебя как оксюморон?

EP>·>В общем, подытожу. Ассерты нужны если:

EP>·>1. Плохо дело с юнит-тестами. Ассерт куда угодно воткнуть легко и хоть какой-то эффект есть.
EP>Ассерты полезны и когда плохо и когда хорошо с юнит-тестами. Работают и при юнит и при интеграционных тестах. Позволяют легко выявлять двойные ошибки и подобное. "Втыкаются" элементарно, и сразу по месту. Плюс служат дополнительной документацией (фактически проверяемой)
Вот и воткни ЮТ. Почему обязательно ассерт?

EP>·>2. Плохо дело с логгированием. Нет нормального способа сообщать о каких-то непредвиденных ситуациях.

EP>Это ты не понимаешь семантику assert'а. Assert это очень жёсткий фейл.
Настолько жесткий, что его можно игнорировать в проде. Ха-ха.

EP>·>3. Плохо дело с обработкой ошибок. Проще грохнуть всё приложение, чем кинуть исключение или нормально обработать ошибочную ситуацию.

EP>·>Т.е. получается что ассерт это такой индикатор code smells.
EP>Нет, и тут не верно. Это уже семантически не assert, так как assert опционален.
EP>А то о чём ты говоришь повсеместно используется в C коде, действительно из-за кривой обработки ошибок, и что характерно не опционально.
EP>Assert это не просто "ошибочная ситуация" а-ля неверный пользовательский ввод и т.п., это БАГ в коде.
Который нужно чинить, а не просто игнорировать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Негодяй  
Дата: 12.08.16 14:28
Оценка:
Здравствуйте, Arsen.Shnurkov, Вы писали:

AS>Собственно, сборка мусора в Rust есть (опционально), а такого мощного статического анализатора в C# нет.


Я маминой мамы маму Раста на твоей спине ....л
Re[32]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 15.08.16 16:24
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

EP>>Да, это преимущество. Но с другой стороны в юнит тестами намного проще перевести код в состояние всех edge-case'ов (в которых в том числе могут выстрелить assert'ы) по сравнению с интеграционными тестами.

CK>В общем случае это недостижимо так как количество edge-case-ов растет экспоненциально, по отношению к количеству ветвлений в программе.

Во-первых необязательно экспоненциально, есть простейшие контр-примеры. Во-вторых количество edge-case'ов зависит далеко не только от ветвлений — их может и вовсе не быть, а edge-case'ов много.
В-третьих речь идёт не про всю программу, а про юнит тесты, которые тестируют отдельные модули — в которых пусть и не всегда, но всё же представляется возможность протестировать все edge-case'ы (а в простых случаях и вовсе все case'ы)

CK>Язык тут роли не играет и вообще ничего не решает.


Да, об этом и речь — в частности assert'ы является общеязыковой техникой.
Re[34]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 15.08.16 17:25
Оценка:
Здравствуйте, ·, Вы писали:

EP>>·>Если код формально верифицируется, то ассерты не нужны. Вообще. Смысл?

EP>>Был код с ассертами -> формально верифицировали его -> выключили ассерты. Это иллюстрация на тему:
·>Если код формально верифицирован — ассерты можно тупо удалить. Формальная верификация — сильная, но дорогая техника доказательства корректности кода, которая полностью заменяет ЮТ и ассерты.

Да, после верификации ассерты можно хоть удалить. Я этот пример привёл именно в качестве иллюстрации на тему опциональности assert'ов

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

·>Если ты вдруг полагаешь, что где-то может быть ошибочное состояние (т.е. хочешь написать там ассерт), то
·>либо воспроизведи это состояние тестом, т.е. ассерт не нужен;

Протестировать внутреннее состояние далеко не всегда легко из вне. Assert же даёт самое ранее обнаружение, при этом крайне дешёв чтобы от него отказываться.
Думаешь все твои тесты делают ненужными написанные ассерты? ОК — в чём проблема их оставить? Когда-нибудь ты, или кто-то другой ошибётся и assert таки выстрелит.

·>либо почеши голову и убедись, что это состояние невозможно, т.е. ассерт не нужен.


assert помимо всего прочего может ловить нарушения preconditions, а конкретнее выстреливать во время интеграции компонентов. Ты не можешь "почесать голову" и убедится в том, что все компоненты (которых ты ещё даже не видел) обязательно будут удовлетворят предусловиям.
Более того, если например ты используешь чужой алгоритм с ассертами на предусловия — то ты фактически получаешь эти все проверки бесплатно.

EP>>>>То есть ты определись — либо у тебя код оттестирован и defensive programming не нужен, либо не оттестирован и ты тестируешь его на своих пользователях

EP>>·>Оттестирован на требуемых спекой сценариях.
EP>>У тебя в "cпеке" описаны все сценарии, для всех компонент на всех уровнях?
·>Зачем на всех уровнях? Ошибка на уровне юнитов будет падать сразу с каким-нибудь bounds checking и видна немедленно и везде.

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

EP>>>>·>При релизном использовании отключены.

EP>>>>Совсем необязательно — я их видел в коммерческом продукте имеющем сотни миллионов установок
EP>>·>Гы-гы. Ведь явно не от хорошей жизни, им тупо пришлось использовать ассерты не по назначению.
EP>>Без понятия — но проблемы в этом нет. Если нужен over-defenesive, то оставляй assert'ы и в release
·>Это не имеет смысла, используют инструмент не по назначению. Это уже тогда не ассерты, а обычные проверки.

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

·>Т.е. придумали себе сложность и героически её решаем.


Это уже твои домыслы что выбор между включением/выключением ассертов это какая-то непомерная сложность требующая героического решения.

·>"Опциональный жесткий фейл". Это не звучит для тебя как оксюморон?


Нет. Нарушение условия ассерта это жёсткий фейл, баг в программе. Точно также как и нарушение любого пред/пост-условия вокруг каждого выражения (а-ля Hoare Triple).
При этом несмотря на то что это жёсткие фейлы, в релизной версии нет даже и малой части всех этих проверок, а некоторые и вовсе неразрешимы. Вот у тебя есть проверки пред/пост-условия вокруг каждого выражения?

EP>>Ассерты полезны и когда плохо и когда хорошо с юнит-тестами. Работают и при юнит и при интеграционных тестах. Позволяют легко выявлять двойные ошибки и подобное. "Втыкаются" элементарно, и сразу по месту. Плюс служат дополнительной документацией (фактически проверяемой)

·>Вот и воткни ЮТ. Почему обязательно ассерт?

Перечитай ещё раз то, на что ответил — например про то что ассерты работают и при интеграционных тестах.
Re[35]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Ночной Смотрящий Россия  
Дата: 15.08.16 17:29
Оценка: +1 :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP> bounds checking это лишь мизерная часть проверки ошибочных состояний, ассерты используются далеко не только для bounds checking.


95% ассертов в тех проектах, которые я хорошо знаю — проверка на null и строк на пустое значение. Это частный случай bounds checking.
Re[36]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 15.08.16 17:52
Оценка: :))
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>95% ассертов в тех проектах, которые я хорошо знаю — проверка на null и строк на пустое значение.


На каком языке?
95% от чего? Запускаемых проверок в рантайме, или от всех проверок выраженных в коде? Если второе, то непонятно зачем их копипастить вместо того чтобы сделать многократно используемый тип а-ля not_null<T>, у которого внутри будет assert.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.