Здравствуйте, velkin, Вы писали:
V>Всё будет убито сиплюсплюсом.
именно так. кремний перестал расти в частотах, на десктопах давно, на мобилках недавно. Выжать максимум из платформы можно будет только переходя на C++. В соотношении абстракция/циклы процессора всем остальным языкам до него как раком до пекина.
Re[23]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, 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-ом
Здравствуйте, 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-ом
Здравствуйте, 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-ом
·>В С++ — другая крайность. Индекс вообще проверяться не будет (вычислили значение i по таблице неправильно и бабах). ·>А если захочешь безопасностьи то вместо vect[i] будешь использовать vect.at(i) и компилятор С++ столкнётся с той же бедой, что и сишарп. ·>По-моему, в подавляющем большинстве случаев важнее не бабахать, чем вырезать.
Ноуп.
В с++ есть диапазоны (boost.range). Там инвариант цикла проверяется один раз и потом уже никаких накладных расходов нет. Помимо этого, выход за границы массива — так себе проблема, я нехочу платить за ее решение в рантайме, если можно этого не делать. Я могу сделать так что индекс не выйдет за границы массива by design. Для этого люди учатся программированию все таки. Нужно уметь рассуждать об инвариантах циклов и подобных вещах. Не думать о корректности и надеяться непонятно на что — не дело.
Re[23]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, 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-ом
Здравствуйте, 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-ом
Здравствуйте, Serginio1, Вы писали:
EP>>То есть ещё раз, код C# чисто по построению намного труднее оптимизировать — для оптимизаций до уровней аналогичных C++ нужно либо язык модифицировать, либо делать оптимизаторы намного более мощные чем оптимизаторы C++ 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-ом
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, Serginio1, Вы писали:
EP>>>То есть ещё раз, код C# чисто по построению намного труднее оптимизировать — для оптимизаций до уровней аналогичных C++ нужно либо язык модифицировать, либо делать оптимизаторы намного более мощные чем оптимизаторы C++ S>> А можно поподробнее чем S>>
S>> И почему для C++ проще занматься оптимизацией?
EP>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче. EP>Виртуальный вызов и прочее можно соптимизировать, но это очевидно более сложная задача чем заинлайнить простой вызов конкретного метода
S>>T4 работают,
EP>Причём тут T4?
А при том, что можно сначала разворачивать исходный код дженериков в код на C# с инлайнингом по типу. Самый простой вариант.
Еще раз мы говорим не использовании дженериков из MSIL. А специализацию дженериков по переданному типу, лямды. Это можно сделать как на уровне исходников. Чем вобщем то и занимаются специализации шаблонов. Я много слышал, что в С++ хотели делать бинарную форму хранения. Что то подобное можно делать и для дженериков.
Я не вижу принципиальной разницы между шаблонами и дженериками. Наоборот дженерики даже проще, так как предоставляют ограничения.
и солнце б утром не вставало, когда бы не было меня
Re[25]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>В этом примере — на C++ вызов конкретного метода, известен размер замыкания и стэковая аллокация. На C# виртуальный вызов, неизвестен размер замыкания и аллокация в куче.
Что за бред? Размер замыкания известен всегда. Аллокация на куче для .NET на порядок дешевле аллокации на куче для C++.
Даже с учетом сборки мусора амортизированная стоимость аллокации невелика.
EP>Виртуальный вызов и прочее можно соптимизировать, но это очевидно более сложная задача чем заинлайнить простой вызов конкретного метода
JIT занимается девиртуализацией вызовов, причем успешно.
Но в примере не будет виртуального вызова, там будет вызов делегата, который сделали офигеть каким медленным из-за того, что поддержку ФВП в C# и VB не планировали изначально.
В F# даже придумали вместо делегатов классы генерировать для ФВП.
Re[3]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, ·, Вы писали:
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-ом
Здравствуйте, 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-ом
EP>Много где, можем попробовать его призвать сюда. Например в CodeJam сделали компонент Assertions. EP>В целом не пойму почему ты проводишь разделение managed/не managed относительно assert'ов.
Ну призвать-то призвал, дальше чего? Я не сильно слежу за веткой, так что пока не понимаю чего обсуждаем.
И тем более не соображу, при чём здесь ассерты bounds checking и прочая мелочёвка — эт всё-таки ответственность компилятора / рантайма / библиотеки типов, но никак не разработчика. Иначе получается вообще замечательное комбо: ручного труда куча, эффект нулевой.
P.S. По хорошему ещё бы и проверки на null на рантайм / компилятор спихнуть, но это года через два будет.
Re[28]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, Sinix, Вы писали:
EP>>Много где, можем попробовать его призвать сюда. Например в CodeJam сделали компонент Assertions. EP>>В целом не пойму почему ты проводишь разделение managed/не managed относительно assert'ов. S>Ну призвать-то призвал, дальше чего? Я не сильно слежу за веткой, так что пока не понимаю чего обсуждаем. S>И тем более не соображу, при чём здесь ассерты bounds checking и прочая мелочёвка — эт всё-таки ответственность компилятора / рантайма / библиотеки типов, но никак не разработчика. Иначе получается вообще замечательное комбо: ручного труда куча, эффект нулевой.
Речи о том чтобы делать везде вручную проверки bounds — нет. Она реализуется один раз внутри vector::operator[]. Точнее уже реализована, её нужно только включить флагом.
Вопрос же к тебе более общий — практика использования assert'ов в managed языках. Насколько я помню ты много раз писал на эту тему.
Re[27]: {@Sinix} Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, 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-ом
Здравствуйте, 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-ом
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Вопрос же к тебе более общий — практика использования assert'ов в managed языках. Насколько я помню ты много раз писал на эту тему.
А чего там обсуждать-то?
Абсолютно стандартная практика: закладываешься на какой-то факт — напиши ассерт. Закладываешься, но считаешь, что "такого не может быть никогда" — напиши отладочный ассерт, сюрприз будет
Дальше ещё проще. Сработал ассерт — останавливаемся и разбираемся в обязательном порядке. Или правим баг в коде (с edit-n-continue можно править код, не прерывая отладки), или убеждаемся, что у нас косяк в дизайне, ставим временный костыль и заводим тикет на исправление.
Не сработал — тож замечательно. Потом сработает. Потому что ассерты работают всегда, а не только один раз в виде конкретного юнит-теста, и, соответственно, шансы словить ошибку у них на порядок больше. На порядок — эт не преувеличение, в том же CodeJam.PerfTests в основном ошибки ловились или интеграционными тестами, или ассертами (хотя их там мало довольно емнип). Простые юнит-тесты главным образом работали как способ обнаружения регрессий и после падения такого теста приходилось ещё разбираться, что именно пошло не так.
Такой подход экономит кучу времени, т.к. для сложного кода можно не раздербанивать кишки на кучу отдельных тестов, а просто прогнать интеграционные тесты для основных сценариев и проследить (через code coverage), что все потенциально проблемные места покрыты.
Вся инструментальная часть требует ровно двух вещей:
* возможность прервать выполнение при нарушении ассерта.
* при подключенном отладчике — немедленно остановиться в месте, где ассерт сломался.
Собственно всё, никакой managed-специфики тут нет.
Re[28]: Да ну и фиг с этой Java-ой. .Net будет убит Rust-ом
Здравствуйте, 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-ом
Здравствуйте, Sinix, Вы писали:
EP>>Вопрос же к тебе более общий — практика использования assert'ов в managed языках. Насколько я помню ты много раз писал на эту тему. S>А чего там обсуждать-то?
Вот это:
·>>>assert вот как-то не прижился в managed языках...
EP>>ЕМНИП Sinix рассказывал что использует их во всю как раз в managed языке.
·>Можно, конечно. Но непонятно зачем. А где он об этом писал?
S>Не сработал — тож замечательно. Потом сработает. Потому что ассерты работают всегда, а не только один раз в виде конкретного юнит-теста, и, соответственно, шансы словить ошибку у них на порядок больше.
Да, это преимущество. Но с другой стороны в юнит тестами намного проще перевести код в состояние всех edge-case'ов (в которых в том числе могут выстрелить assert'ы) по сравнению с интеграционными тестами.
S>Такой подход экономит кучу времени, т.к. для сложного кода можно не раздербанивать кишки на кучу отдельных тестов, а просто прогнать интеграционные тесты для основных сценариев и проследить (через code coverage), что все потенциально проблемные места покрыты.
100% code coverage не даёт гарантии попадания во все edge cases.