Re[2]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: antropolog  
Дата: 09.08.16 23:22
Оценка: 2 (1) :)
Здравствуйте, velkin, Вы писали:

V>Всё будет убито сиплюсплюсом.


именно так. кремний перестал расти в частотах, на десктопах давно, на мобилках недавно. Выжать максимум из платформы можно будет только переходя на C++. В соотношении абстракция/циклы процессора всем остальным языкам до него как раком до пекина.
Re[23]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 10.08.16 07:31
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>>>>>Здесь внутри foo, ещё до всяких оптимизаторов, известен и конкретный тип замыкания F, и его размер, и конкретный вызываемый метод.

EP>>>>>Аналог на C# в студию
S>>>>
S>>>> T foo<T>(Func<T> f)
S>>>>{
S>>>>  return f();
S>>>>}
S>>>>

EP>>>О чём и речь — у тебя внутри foo один тип для разных замыканий с одинаковой сигнатурой — а значит динамический полиморфизм и прочие индерекции, которые на порядок сложнее оптимизировать
S>> На данном этапе да.
S>>Но мы то говорим о компиляторе в стиле C++. А там
return x>>
проинлайнить не проблема на уровне исходников.


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

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

А можно поподробнее чем

 T foo<T>(Func<T> f)
  {
  return f();
  }

Принципиально отличается

template<typename F>
auto foo(F f)
{
    return f();
}


И почему для C++ проще занматься оптимизацией? T4 работают, а создать генератор кода на дженериках тоже не огромная проблема. Да для кое где нужно что то добавить в язык для более удобного использования ограничений. Но это небольшие нововведения.
и солнце б утром не вставало, когда бы не было меня
Re[21]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 10.08.16 07:33
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

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


S>> Еще раз повторю. Время идет и все меняется. Те же циклы типа

S>>
S>>for(int i=0;i<ar.Length,i++)
S>>    var a=ar[i];
S>>

S>>Никаких проверок не будет. А для работы с матрицами есть SIMD.

CK>Компилятор С++ вообще этот цикл вырежет, так как он ничего не делает. Либо векторизует, если `a` дальше используется в вычислениях, причем вкорячит туда такой код, который сможет работать даже в случае если массив не выровнен. Ну а компилятор сишарпа даже проверки сможет вырезать только в простых случаях (линейно бежим по массиву). Если есть inderection (вычисляем значение i по таблице, например) — то он не вырежет ничего и будет каждый индекс проверять.


Еще раз. MS задумали сделать нативный компилятор для C# по аналогии с C++. Плюс сборка мусора. Как получится не знаю, но движение есть.
и солнце б утром не вставало, когда бы не было меня
Re[25]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: · Великобритания  
Дата: 10.08.16 09:12
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>Есть третий вариант: во время разработки и тестирования включаются asserts, checked iterators и прочий defensive programming,

EP>·>А во время эксплуатации что делать? Молитву о здравии заказывать?
EP>Тестировать код — это нужно в любом случае. Все эти defensive примочки от багов не исцеляют, а лишь смягчают последствия фейла.
Они фейл делают фейлом, а не UB.

EP>·>Довольно рисково иметь разный код в дебаге и релизе.

EP>Тестируй и debug и release если есть какие-то сомнения в отсутсвии side-effect'ов у assert'ов.
Тут не в сомнениях дело. А сам факт того, что если условие ассерта не сработает в релизе — то угадать что будет — невозможно.

EP>·>assert вот как-то не прижился в managed языках...

EP>ЕМНИП Sinix рассказывал что использует их во всю как раз в managed языке.
Можно, конечно. Но непонятно зачем. А где он об этом писал?

EP>·>Мало они помогают. Переносят проблемы с плеч компилятора на плечи программиста. Угадай — кто чаще ошибается?

EP>Задача писать корректный код лежит на плечах программиста, и никакие defensive штуки не превратят некорректный код в корректный. Максимум в чём они помогают — это раннее определение уже свершившегося бага, да и то в рантайме
Они позволяют избежать UB. Грубо говоря некий такой wysiwyg — как код написан, так он и исполняется. А не так, что вышли за границы массива или обратились к неинициализированной памяти — и последствия совершенно непредсказуемы.
Даже вон JMM придумали, чтобы для многопоточного кода всё было предсказуемо.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[22]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: chaotic-kotik  
Дата: 10.08.16 09:16
Оценка: +1
Здравствуйте, ·, Вы писали:


·>В С++ — другая крайность. Индекс вообще проверяться не будет (вычислили значение i по таблице неправильно и бабах).

·>А если захочешь безопасностьи то вместо vect[i] будешь использовать vect.at(i) и компилятор С++ столкнётся с той же бедой, что и сишарп.
·>По-моему, в подавляющем большинстве случаев важнее не бабахать, чем вырезать.

Ноуп.
В с++ есть диапазоны (boost.range). Там инвариант цикла проверяется один раз и потом уже никаких накладных расходов нет. Помимо этого, выход за границы массива — так себе проблема, я нехочу платить за ее решение в рантайме, если можно этого не делать. Я могу сделать так что индекс не выйдет за границы массива by design. Для этого люди учатся программированию все таки. Нужно уметь рассуждать об инвариантах циклов и подобных вещах. Не думать о корректности и надеяться непонятно на что — не дело.
Re[23]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: · Великобритания  
Дата: 10.08.16 09:36
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>·>В С++ — другая крайность. Индекс вообще проверяться не будет (вычислили значение i по таблице неправильно и бабах).

CK>·>А если захочешь безопасностьи то вместо vect[i] будешь использовать vect.at(i) и компилятор С++ столкнётся с той же бедой, что и сишарп.
CK>·>По-моему, в подавляющем большинстве случаев важнее не бабахать, чем вырезать.
CK>Ноуп.
CK>В с++ есть диапазоны (boost.range). Там инвариант цикла проверяется один раз и потом уже никаких накладных расходов нет.
CK>Помимо этого, выход за границы массива — так себе проблема, я нехочу платить за ее решение в рантайме, если можно этого не делать. Я могу сделать так что индекс не выйдет за границы массива by design. Для этого люди учатся программированию все таки. Нужно уметь рассуждать об инвариантах циклов и подобных вещах. Не думать о корректности и надеяться непонятно на что — не дело.
Так Bounds-checking elimination это тоже умеет делать, конечно, в простых случаях только (как и типичный программист), зато гарантированно не ошибается.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[25]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.08.16 10:50
Оценка: 1 (1)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


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

G>>Язык тут не при чем.

EP>Именно язык тут и при чём. Если писать на C++ в стиле C# — динамические замыкания, динамический IEnumerable, множество индерекций по памяти — то получим примерно такие же тормоза

Что такое "данимические замыкания" ? В IL замыкания превращаются просто в классы.
Что такое "динамический IEnumerable"?
Это в смысле IEnumerable не по массивам? Зачем его использовать в performance-critical коде? А IEnumerable (foreach) с массивами действительно тормозит (накладные расходы на Enumerator оказываются больше, чем затратры на тело цикла). Как раз из-за недостатка инлайнинга, в C++ foreach нилайнится.
Или ты имеешь ввиду использование ФВП для обхода массивов? Оно действительно и в C++, и в C# плохо работает. И в C++ и в C# такой код вручную оптимизируется.


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

G>>Но у JIT нет столько времени на пребразования, как у компилятора C++.
EP>Что мешает сразу генерировать оптимизированный IL?
IL — высокоуровневый язык. Он и так достаточно оптимален на своем уровне. Кроме того IL еще и метаинформацию хранить должен.

EP>Я выше привёл пример с трансляцией C++ -> JS. JS ещё "хуже" IL, но тем не менее он работает быстро.

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


EP>Можно взять этот JS выхлоп и перевести на C# — и он там тоже будет работать быстро, несмотря ни на какой IL

Если не используются ФВП, то да. Вообще при небольших усилиях скорость C# аналогична C++. Даже статью на эту тему писал https://habrahabr.ru/post/266373/
Re[24]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 10.08.16 11:29
Оценка:
Здравствуйте, Serginio1, Вы писали:

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

S> А можно поподробнее чем
S>
S> T foo<T>(Func<T> f)
S>  {
S>  return f();
S>  }
S>

S>Принципиально отличается
S>
S>template<typename F>
S>auto foo(F f)
S>{
S>    return f();
S>}
S>


Тем что на C# после всех подстановок схематически получается:
class AbstractF 
{
public:
    virtual int virtual_call() = 0;
};

int foo(AbstractF *f)
{
    return f->virtual_call();
}
А на C++:
struct ConcreteF 
{
    int x;
    int concrete_call()
    {
        return x;
    }
};

int foo(ConcreteF f)
{
    return f.concrete_call();
}


S> И почему для C++ проще занматься оптимизацией?


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

S>T4 работают,


Причём тут T4?
Re[25]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 10.08.16 11:40
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


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

S>> А можно поподробнее чем
S>>
S>> T foo<T>(Func<T> f)
S>>  {
S>>  return f();
S>>  }
S>>

S>>Принципиально отличается
S>>
S>>template<typename F>
S>>auto foo(F f)
S>>{
S>>    return f();
S>>}
S>>


EP>Тем что на C# после всех подстановок схематически получается:

EP>
EP>class AbstractF 
EP>{
EP>public:
EP>    virtual int virtual_call() = 0;
EP>};

EP>int foo(AbstractF *f)
EP>{
EP>    return f->virtual_call();
EP>}
EP>
А на C++:

EP>
EP>struct ConcreteF 
EP>{
EP>    int x;
EP>    int concrete_call()
EP>    {
EP>        return x;
EP>    }
EP>};

EP>int foo(ConcreteF f)
EP>{
EP>    return f.concrete_call();
EP>}
EP>


S>> И почему для C++ проще занматься оптимизацией?


EP>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче.

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

S>>T4 работают,


EP>Причём тут T4?

А при том, что можно сначала разворачивать исходный код дженериков в код на C# с инлайнингом по типу. Самый простой вариант.
Еще раз мы говорим не использовании дженериков из MSIL. А специализацию дженериков по переданному типу, лямды. Это можно сделать как на уровне исходников. Чем вобщем то и занимаются специализации шаблонов. Я много слышал, что в С++ хотели делать бинарную форму хранения. Что то подобное можно делать и для дженериков.

Я не вижу принципиальной разницы между шаблонами и дженериками. Наоборот дженерики даже проще, так как предоставляют ограничения.
и солнце б утром не вставало, когда бы не было меня
Re[25]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.08.16 11:44
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче.

Что за бред? Размер замыкания известен всегда. Аллокация на куче для .NET на порядок дешевле аллокации на куче для C++.
Даже с учетом сборки мусора амортизированная стоимость аллокации невелика.


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

JIT занимается девиртуализацией вызовов, причем успешно.

Но в примере не будет виртуального вызова, там будет вызов делегата, который сделали офигеть каким медленным из-за того, что поддержку ФВП в C# и VB не планировали изначально.
В F# даже придумали вместо делегатов классы генерировать для ФВП.
Re[3]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Mihas  
Дата: 10.08.16 11:44
Оценка:
Здравствуйте, antropolog, Вы писали:

A>кремний перестал расти в частотах,

Да ну??
А вот тут до 16 ГГц выжимают.

  --
шутка, конечно же
Re[26]: {@Sinix} Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 10.08.16 12:02
Оценка:
Здравствуйте, ·, Вы писали:

EP>>·>Довольно рисково иметь разный код в дебаге и релизе.

EP>>Тестируй и debug и release если есть какие-то сомнения в отсутсвии side-effect'ов у assert'ов.
·>Тут не в сомнениях дело. А сам факт того, что если условие ассерта не сработает в релизе — то угадать что будет — невозможно.

assert'ы применяются намного шире чем простейший bounds checking — для проверки пред/пост-условий, для проверки инвариантов и вариантов циклов, для проверки инвариантов классов и т.п.
И да, если твой вариант цикла неверный, а assert'ы либо выключены, либо вообще не написаны, либо бывает что их вообще невозможно выразить в коде — то действительно угадать что будет невозможно, это типичный баг в программе
Вопрос к тебе — когда ты написал последний assert/проверку для варианта цикла? Если ты не пишешь проверку каких-то свойств корректности программы, это не значит что их нет, и да — здесь применим весь твой пессимизм "угадать что будет — невозможно"
С другой стороны, выключение части из проверок в runtime'е, ровно как и отсутствии часть проверок вообще — не делает программу некорректной.

EP>>·>assert вот как-то не прижился в managed языках...

EP>>ЕМНИП Sinix рассказывал что использует их во всю как раз в managed языке.
·>Можно, конечно. Но непонятно зачем. А где он об этом писал?

Много где, можем попробовать его призвать сюда. Например в CodeJam сделали компонент Assertions.
В целом не пойму почему ты проводишь разделение managed/не managed относительно assert'ов.

EP>>·>Мало они помогают. Переносят проблемы с плеч компилятора на плечи программиста. Угадай — кто чаще ошибается?

EP>>Задача писать корректный код лежит на плечах программиста, и никакие defensive штуки не превратят некорректный код в корректный. Максимум в чём они помогают — это раннее определение уже свершившегося бага, да и то в рантайме
·>Они позволяют избежать UB. Грубо говоря некий такой wysiwyg — как код написан, так он и исполняется. А не так, что вышли за границы массива или обратились к неинициализированной памяти — и последствия совершенно непредсказуемы.

Это лишь только для мизерной части утверждений корректности программы. Если ты не расписал все утверждения корректности, то никакого "WYSIWYG" — очевидно нет

P.S. Ничто не мешает оставить часть assert'ов включенных в Release. Для этого их обычно разбивают на классы "быстрые", "медленные", "очень медленные" — и уже пользователь библиотеки решает что попадёт в Release.
Re[26]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 10.08.16 12:20
Оценка:
Здравствуйте, gandjustas, Вы писали:

EP>>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче.

G>Что за бред? Размер замыкания известен всегда.

Ты не понимаешь о чём речь, речь о том что видно внутри шаблона/дженерика после всех подстановок. В C++ размер замыкания известен внутри foo во время компиляции:
int foo(ConcreteF f)
{
    static_assert(sizeof(ConcreteF) == sizeof(int))
    return f.concrete_call();
}

В C# же у тебя будет Func<...> одинакового размера для всех вариантов замыканий, независимо от их оригинального размера..

G>Аллокация на куче для .NET на порядок дешевле аллокации на куче для C++.


1. Она всё равно дороже чем отсутствие аллокации в куче. На C++ может не быть и даже стэковой аллокаций — всё передастся через регистры, либо и вовсе не будет никакой передачи, а лишь подстановка константы.
2. Речь даже не столько про стоимость аллокации, а про то что она сильно мешает последующему инлайнингу.

G>Даже с учетом сборки мусора амортизированная стоимость аллокации невелика.


Она огромна по сравнению с отсуствием аллокаций

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

G>JIT занимается девиртуализацией вызовов, причем успешно.

Ты читать не умеешь? "это очевидно более сложная задача"
А так не только JIT инлайнит виртуальные вызовы, но и обычный статический компилятор, даже без всяких PGO — но работает это далеко не всегда.

G>Но в примере не будет виртуального вызова, там будет вызов делегата, который сделали офигеть каким медленным из-за того, что поддержку ФВП в C# и VB не планировали изначально.

G>В F# даже придумали вместо делегатов классы генерировать для ФВП.

В любом случае это будет медленная динамическая хрень, против обычного вызова невиртуальной функции. И эту хрень намного тяжелее оптимизировать, простым преобразования C# -> C++ не отделаешься
Re[27]: {@Sinix} Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Sinix  
Дата: 10.08.16 12:25
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Много где, можем попробовать его призвать сюда. Например в CodeJam сделали компонент Assertions.

EP>В целом не пойму почему ты проводишь разделение managed/не managed относительно assert'ов.

Ну призвать-то призвал, дальше чего? Я не сильно слежу за веткой, так что пока не понимаю чего обсуждаем.
И тем более не соображу, при чём здесь ассерты bounds checking и прочая мелочёвка — эт всё-таки ответственность компилятора / рантайма / библиотеки типов, но никак не разработчика. Иначе получается вообще замечательное комбо: ручного труда куча, эффект нулевой.

P.S. По хорошему ещё бы и проверки на null на рантайм / компилятор спихнуть, но это года через два будет.
Re[28]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 10.08.16 12:40
Оценка: +1
Здравствуйте, Sinix, Вы писали:

EP>>Много где, можем попробовать его призвать сюда. Например в CodeJam сделали компонент Assertions.

EP>>В целом не пойму почему ты проводишь разделение managed/не managed относительно assert'ов.
S>Ну призвать-то призвал, дальше чего? Я не сильно слежу за веткой, так что пока не понимаю чего обсуждаем.
S>И тем более не соображу, при чём здесь ассерты bounds checking и прочая мелочёвка — эт всё-таки ответственность компилятора / рантайма / библиотеки типов, но никак не разработчика. Иначе получается вообще замечательное комбо: ручного труда куча, эффект нулевой.

Речи о том чтобы делать везде вручную проверки bounds — нет. Она реализуется один раз внутри vector::operator[]. Точнее уже реализована, её нужно только включить флагом.

Вопрос же к тебе более общий — практика использования assert'ов в managed языках. Насколько я помню ты много раз писал на эту тему.
Re[27]: {@Sinix} Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: · Великобритания  
Дата: 10.08.16 12:44
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>·>Тут не в сомнениях дело. А сам факт того, что если условие ассерта не сработает в релизе — то угадать что будет — невозможно.

EP>assert'ы применяются намного шире чем простейший bounds checking — для проверки пред/пост-условий, для проверки инвариантов и вариантов циклов, для проверки инвариантов классов и т.п.
А что они дают-то? Почему вместо assert x просто нельзя if(!x) throw Boom()? Если медленно — это уже проблема алгоритма. При хорошем покрытии тестами ценность ассертов падает до нуля.

EP>И да, если твой вариант цикла неверный, а assert'ы либо выключены, либо вообще не написаны, либо бывает что их вообще невозможно выразить в коде — то действительно угадать что будет невозможно, это типичный баг в программе

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

EP>С другой стороны, выключение части из проверок в runtime'е, ровно как и отсутствии часть проверок вообще — не делает программу некорректной.

Но только если ассерты расставлены корректно.

EP>>>ЕМНИП Sinix рассказывал что использует их во всю как раз в managed языке.

EP>·>Можно, конечно. Но непонятно зачем. А где он об этом писал?
EP>Много где, можем попробовать его призвать сюда. Например в CodeJam сделали компонент Assertions.
Я что-то не понял что это. Судя по if (!condition)throw это не ассерты в понимании С++.

EP>В целом не пойму почему ты проводишь разделение managed/не managed относительно assert'ов.

Это в ответ на то, что в С++ "включаются asserts, checked iterators и прочий defensive programming". Ассерты тут как частный случай скорее.
Т.е. в managed у тебя всегда работает правильно, а когда может — работает быстро. В unmanaged наоборот: когда может — работает правильно, и всегда быстро.

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

EP>·>Они позволяют избежать UB. Грубо говоря некий такой wysiwyg — как код написан, так он и исполняется. А не так, что вышли за границы массива или обратились к неинициализированной памяти — и последствия совершенно непредсказуемы.
EP>Это лишь только для мизерной части утверждений корректности программы. Если ты не расписал все утверждения корректности, то никакого "WYSIWYG" — очевидно нет
Есть конечно. Я говорю о корректности самого кода, а не логических ошибок в алгоритме. Т.е. в С++ если видишь, что у тебя в коде написано a = b + c то при известных значениях b==1 и c==2 может случиться, что a!=3 т.к. где-то что-то стрельнуло по памяти из другого треда. В managed — a==3 всегда, доказано верификатором.

EP>P.S. Ничто не мешает оставить часть assert'ов включенных в Release. Для этого их обычно разбивают на классы "быстрые", "медленные", "очень медленные" — и уже пользователь библиотеки решает что попадёт в Release.

По-моему излишнее усложнение. Собственно на основании чего пользователь будет принимать решение?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[27]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.08.16 13:02
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>>>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче.

G>>Что за бред? Размер замыкания известен всегда.

EP>Ты не понимаешь о чём речь, речь о том что видно внутри шаблона/дженерика после всех подстановок. В C++ размер замыкания известен внутри foo во время компиляции:

Это ты не понимаешь о чем пишешь. В C# для замыканий генерируется класс, размер которого тоже известен во время компиляции.

EP>В C# же у тебя будет Func<...> одинакового размера для всех вариантов замыканий, независимо от их оригинального размера..

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


G>>Аллокация на куче для .NET на порядок дешевле аллокации на куче для C++.


EP>1. Она всё равно дороже чем отсутствие аллокации в куче. На C++ может не быть и даже стэковой аллокаций — всё передастся через регистры, либо и вовсе не будет никакой передачи, а лишь подстановка константы.

Это если вызов "делегата" будет заинлайнен, но в .NET такого не происходит. Дело вовсе не в языке, а в том, как и в каких условиях работает JIT.

EP>2. Речь даже не столько про стоимость аллокации, а про то что она сильно мешает последующему инлайнингу.

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

G>>Даже с учетом сборки мусора амортизированная стоимость аллокации невелика.

EP>Она огромна по сравнению с отсуствием аллокаций
В реальной программе аллокаций будет дофига в любом случае.

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

G>>JIT занимается девиртуализацией вызовов, причем успешно.
EP>Ты читать не умеешь? "это очевидно более сложная задача"
С чего ты взял что она сложная? Почти примитивная оптимизация.


G>>Но в примере не будет виртуального вызова, там будет вызов делегата, который сделали офигеть каким медленным из-за того, что поддержку ФВП в C# и VB не планировали изначально.

G>>В F# даже придумали вместо делегатов классы генерировать для ФВП.
EP>В любом случае это будет медленная динамическая хрень, против обычного вызова невиртуальной функции. И эту хрень намного тяжелее оптимизировать, простым преобразования C# -> C++ не отделаешься
Это надо проверить.
Re[29]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Sinix  
Дата: 10.08.16 13:18
Оценка: 2 (1)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Вопрос же к тебе более общий — практика использования assert'ов в managed языках. Насколько я помню ты много раз писал на эту тему.


А чего там обсуждать-то?

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

Дальше ещё проще. Сработал ассерт — останавливаемся и разбираемся в обязательном порядке. Или правим баг в коде (с edit-n-continue можно править код, не прерывая отладки), или убеждаемся, что у нас косяк в дизайне, ставим временный костыль и заводим тикет на исправление.

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

Такой подход экономит кучу времени, т.к. для сложного кода можно не раздербанивать кишки на кучу отдельных тестов, а просто прогнать интеграционные тесты для основных сценариев и проследить (через code coverage), что все потенциально проблемные места покрыты.


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

Собственно всё, никакой managed-специфики тут нет.
Re[28]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 10.08.16 13:19
Оценка: -1 :)
Здравствуйте, gandjustas, Вы писали:

EP>>>>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче.

G>>>Что за бред? Размер замыкания известен всегда.
EP>>Ты не понимаешь о чём речь, речь о том что видно внутри шаблона/дженерика после всех подстановок. В C++ размер замыкания известен внутри foo во время компиляции:
G>Это ты не понимаешь о чем пишешь. В C# для замыканий генерируется класс, размер которого тоже известен во время компиляции.

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

EP>>В C# же у тебя будет Func<...> одинакового размера для всех вариантов замыканий, независимо от их оригинального размера..

G>Ты наверное что-то другое хочешь сказать, но не можешь это словами выразить.

Я уже выразил, и пример привёл. Но ты, из-за своего неконструктивного настроя, не можешь это понять.

EP>>2. Речь даже не столько про стоимость аллокации, а про то что она сильно мешает последующему инлайнингу.

G>Ты причину и следствие путаешь.

Не путаю.

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


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

G>>>Даже с учетом сборки мусора амортизированная стоимость аллокации невелика.

EP>>Она огромна по сравнению с отсуствием аллокаций
G>В реальной программе аллокаций будет дофига в любом случае.

Это на C# дофига аллокаций, на C++ их на порядки меньше, так как многое хранится по значению. Вектор объектов класса — это минимум одна аллокация, на C# — минимум N+1 аллокаций.
И в любом случае 100*дофига намного больше чем просто дофига

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

G>>>JIT занимается девиртуализацией вызовов, причем успешно.
EP>>Ты читать не умеешь? "это очевидно более сложная задача"
G>С чего ты взял что она сложная? Почти примитивная оптимизация.

И всё таки ты читать не умеешь "более сложная"

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

G>Это надо проверить.

Что конкретно?
Re[30]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
От: Evgeny.Panasyuk Россия  
Дата: 10.08.16 13:36
Оценка: 2 (1) +1
Здравствуйте, Sinix, Вы писали:

EP>>Вопрос же к тебе более общий — практика использования assert'ов в managed языках. Насколько я помню ты много раз писал на эту тему.

S>А чего там обсуждать-то?

Вот это:

·>>>assert вот как-то не прижился в managed языках...
EP>>ЕМНИП Sinix рассказывал что использует их во всю как раз в managed языке.
·>Можно, конечно. Но непонятно зачем. А где он об этом писал?


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


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

S>Такой подход экономит кучу времени, т.к. для сложного кода можно не раздербанивать кишки на кучу отдельных тестов, а просто прогнать интеграционные тесты для основных сценариев и проследить (через code coverage), что все потенциально проблемные места покрыты.


100% code coverage не даёт гарантии попадания во все edge cases.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.