Здравствуйте, Evgeny.Panasyuk, Вы писали:
G>>Что такое "данимические замыкания" ? В IL замыкания превращаются просто в классы.
EP>При этом передаются через параметры, сохраняются в классы и т.п. посредством динамического полиморфизма, стирая исходный тип.
Ты бредишь
1) В замыканиях нетполиморфизма
2) В замыканиях нет женериков
3) В .NET вообще нет стирания типов в женериках
G>>Что такое "динамический IEnumerable"?
EP>Динамический полиморфизм для обхода последовательностей.
Пример кода покажи, не ясно о чем ты пишешь.
G>>Это в смысле IEnumerable не по массивам? Зачем его использовать в performance-critical коде?
EP>Затем что последовательности могут быть выражены разными структурами данных.
Не пойму о чем ты. Если у тебя массив или строка — пиши обычный цикл. Если там не массив и не строка, то пофиг — IEnumerable нормально работает.
EP>Собственно о том и речь — в C++ я могу накручивать уровни абстракций даже в performance-critical коде, без жёсткого penalty.
Это только если инлайнинг сработает. Об этом тебе и писали, дело не в языке, а в компиляторе. К сожалению для .NET негде такую оптимизацию производить.
G>>А IEnumerable (foreach) с массивами действительно тормозит (накладные расходы на Enumerator оказываются больше, чем затратры на тело цикла). Как раз из-за недостатка инлайнинга, в C++ foreach нилайнится.
EP>Так дело не в самом foreach — ты если распишешь его вручную, то он также будет тормозить, за счёт динамического полиморфизма..
Для обычных массивов jit оптимизирует foreach
var xs = new[] { 1, 2, 3 };
var s = 0;
foreach (var item in xs)
{
s += item;
}
выхлоп:
var s = 0;
019A34D5 xor esi,esi
foreach (var item in xs)
019A34D7 xor ecx,ecx
019A34D9 mov edi,dword ptr [edx+4]
019A34DC test edi,edi
019A34DE jle 019A34EB
foreach (var item in xs)
019A34E0 mov eax,dword ptr [edx+ecx*4+8]
{
s += item;
019A34E4 add esi,eax
019A34E6 inc ecx
foreach (var item in xs)
019A34E7 cmp edi,ecx
019A34E9 jg 019A34E0
}
Найди тут виртуальные вызовы.
Для не-массивов применяется оптимизация:
1) Реализация IEnumerator делается структурой
2) GetEnumerator возвращает структуру
3) foreach в этом случае не использует ни аллокаций, ни приведений к интерфейсу, ни полиморфизм
C IEnumerable другая проблема, которая связана с ФВП. Вообще ФП в C# довольно медленно работает, не создавался язык под него.
G>>Или ты имеешь ввиду использование ФВП для обхода массивов? Оно действительно и в C++, и в C# плохо работает. И в C++ и в C# такой код вручную оптимизируется.
EP>ФВП в том числе. А с чего ты взял что они в C++ плохо работают? Наоборот, прекрасно работают и отлично оптимизируются.
EP>Пример C++ -> JS vs C# как раз ФВП и использует.
За счет инлайнинга, который JIT не делает.
Но ты же не будешь в performance-critical на автоматический инлайнинг полагаться.
G>>>>Машинный код генерируется из IL. В нем все указанные тобой оптимизации делаются элементарно.
G>>>>Но у JIT нет столько времени на пребразования, как у компилятора C++.
EP>>>Что мешает сразу генерировать оптимизированный IL?
G>>IL — высокоуровневый язык. Он и так достаточно оптимален на своем уровне.
EP>И что из этого? C# версия без ФВП тоже компилируется в тот же самый "высокоуровневый IL", но работает быстрее. Версия с ФВП могла бы давать точно такой же IL.
Не могла бы, потому что делегаты. В F# отказались от делегатов, там лучше работает.
EP>>>Я выше привёл пример с трансляцией C++ -> JS. JS ещё "хуже" IL, но тем не менее он работает быстро.
G>>Что "хуже" ?
EP>Тем что JS более высокоуровневый.
И что?
G>>Работает быстро за счет "hotspot". Большинство движков JS перекомпилируют код на основании профиля использования. Кроме того "работает быстро" — это когда не используются ФВП.
EP>Вот именно, в исходном коде на C++ были ФВП и замыкание, в результирующем JS они были выоптимизированы компилятором C++, так как это элементарная операция (никакого динамического полиморфизма) — именно поэтому и быстро.
Ну так ты продолжаешь доказывать, что проблема в языке C#. А проблема в компиляторе, который ФВП не инлайнит. О чем я тебе и говорил с самого начала.