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

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

G>>>Язык тут не при чем.
EP>>Именно язык тут и при чём. Если писать на C++ в стиле C# — динамические замыкания, динамический IEnumerable, множество индерекций по памяти — то получим примерно такие же тормоза
G>Что такое "данимические замыкания" ? В IL замыкания превращаются просто в классы.

При этом передаются через параметры, сохраняются в классы и т.п. посредством динамического полиморфизма, стирая исходный тип.

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


Динамический полиморфизм для обхода последовательностей.

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


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

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


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

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


ФВП в том числе. А с чего ты взял что они в C++ плохо работают? Наоборот, прекрасно работают и отлично оптимизируются.
Пример C++ -> JS vs C# как раз ФВП и использует.

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

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

И что из этого? C# версия без ФВП тоже компилируется в тот же самый "высокоуровневый IL", но работает быстрее. Версия с ФВП могла бы давать точно такой же IL.

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

G>Что "хуже" ?

Тем что JS более высокоуровневый.

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


Вот именно, в исходном коде на C++ были ФВП и замыкание, в результирующем JS они были выоптимизированы компилятором C++, так как это элементарная операция (никакого динамического полиморфизма) — именно поэтому и быстро.

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

G>Если не используются ФВП, то да.

ФВП в коде на C++ есть, но в выхлопе оптимизатора их нет.

G>Вообще при небольших усилиях скорость C# аналогична C++. Даже статью на эту тему писал https://habrahabr.ru/post/266373/


Твой тест низкоуровневый, без абстракций, и полностью подтверждает мой тезис, точнее вторую часть:

C++ быстрый не от того что нативный, а от того что даёт бесплатные, либо крайне дешёвые абстракции.
Если на C#/Java писать код без абстракций, то по скорости он будет близок к аналогу на C++ — но такого кода будет намного больше

А вот первую часть ты можешь проверить сделав сортировку похожей на реальную, а именно абстрагировавшись от конкретного контейнера, конкретного типа элемента, и конкретного предиката. Например сравнив:
template<typename RandomAccessIterator, typename BinaryPredicate>
void sort(RandomAccessIterator m, unsigned n, BinaryPredicate p)
vs
void Sort(IList<T> m, Comparison<T> comparison)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.