Здравствуйте, Blazkowicz, Вы писали:
RAZ>>Тот же GC, очень эффективно собирает объекты по сравнению с вызовом кучи деструкторов в С++.
Если в С++ у некоего типа тривиальный деструктор, то он не вызывается. А если во время разрушения должен быть выполнен некий код, то он должен быть выполнен на любом языке.
Среды с GC делают одну интересную вещь — создают объекты в одном потоке, а удаляют из другого. В С++ тоже используют подобные механизмы при надобности — такое поведение хорошо подходит для систем с характерными неравномерными нагрузками, которые можно сгладить за счет памяти.
Опять же, системные менеджеры памяти, начиная еще со времен Висты, уже много лет как сделали ненужным разработку таких самописных менеджеров памяти, которые писались исключительно для целей, скажем, "однопоточности", т.е. для того, чтобы разные потоки не сталкивались на мьютексе глобального менеджера памяти С/С++. В мире Линукс примерно в то же самое время происходили аналогичные изменения.
Т.е., в плане конкурентной работы у нейтивных менеджеров в последние годы всё очень даже неплохо.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
A>>У меня давно сложилось и продолжает укрепляться подозрение, что C++ ощутимо (хотя бы раза в два) быстрее C# только там, где повезёт и задачу можно векторизовать, и компилятор C++ сумеет при этом применить свою векторизовальную магию. А если не повезёт, то быстрее может и вообще не получиться.
EP>1. Возьми тест из обсуждаемой статьи и сделай нормальный интерфейс для сортировки (например как у std::sort) — позволяющий сортировать последовательности из разных типов контейнеров, с разными типами элементов, и с разными предикатами сравнения, задаваемыми например замыканиями. Для пущей уверенности выключи любую автовекторизацию при замерах.
Моё большое имхо: код, который оперирует разными универсальными обобщёнными абстракциями, ― это те 90%, от которых не требуется скорости. Он пишется так, чтобы было легко писать, читать и поддерживать, без оглядки на производительность. Если явно не тормозит, то и замечательно. По крайней мере, у меня дела обычно обстоят именно так.
Сравнивать скорость имеет смысл у того кода, который в самом деле её требует и который пишется с оглядкой на скорость, а не на удобство, и использует все доступные в языке средства оптимизации.
EP>3. Ещё такой пример
: высокоуровневый код C++ скомпилированный в JavaScript почти в два раза быстрее аналога на C# в своей родной среде. EP>JavaScript, в веб-браузере, Карл!
я нашёл рядом два исходника на C++ и C#. У варианта С++ в настройках Optimization включил всё, что было на максимум. У варианта на C# сделал очевидную оптимизацию в коде (ценой в несколько миллисекунд), поменял
for (int i = 0; i < n; i++)
v[i] = v[i] * u[i];
на
for (int i = 0; i < v.Length; i++)
v[i] *= u[i];
Запустил оба варианта раз по десять, лучшие результаты такие:
Здравствуйте, alexzz, Вы писали:
EP>>1. Возьми тест из обсуждаемой статьи и сделай нормальный интерфейс для сортировки (например как у std::sort) — позволяющий сортировать последовательности из разных типов контейнеров, с разными типами элементов, и с разными предикатами сравнения, задаваемыми например замыканиями. Для пущей уверенности выключи любую автовекторизацию при замерах. A>Моё большое имхо: код, который оперирует разными универсальными обобщёнными абстракциями, ― это те 90%, от которых не требуется скорости. Он пишется так, чтобы было легко писать, читать и поддерживать, без оглядки на производительность. Если явно не тормозит, то и замечательно. По крайней мере, у меня дела обычно обстоят именно так.
А у меня тот код который быстрый обычно оперирует обобщёнными абстракциями — это более высокий уровень — because I can. И в таком коде я спокойно могу использовать например ФВП и замыкания, а не расписывать вручную каждую комбинацию алгоритма, контейнера и предиката — because I can.
A>Сравнивать скорость имеет смысл у того кода, который в самом деле её требует и который пишется с оглядкой на скорость, а не на удобство, и использует все доступные в языке средства оптимизации.
Это же одна из главных фишек C++ — удобно писать быстрый код. Цитата в тему:
"The going word at Facebook is that 'reasonably written C++ code just runs fast,' which underscores the enormous effort spent at optimizing PHP and Java code. Paradoxically, C++ code is more difficult to write than in other languages, but efficient code is a lot easier [to write in C++ than in other languages]." – Herb Sutter at //build/, quoting Andrei Alexandrescu
Вообще, это распространённое заблуждение что быстрый код синоним низкоуровневого.
EP>>3. Ещё такой пример
: высокоуровневый код C++ скомпилированный в JavaScript почти в два раза быстрее аналога на C# в своей родной среде. EP>>JavaScript, в веб-браузере, Карл! A>Вот в этом посте
я нашёл рядом два исходника на C++ и C#. У варианта С++ в настройках Optimization включил всё, что было на максимум. У варианта на C# сделал очевидную оптимизацию в коде (ценой в несколько миллисекунд), поменял A>
for (int i = 0; i < n; i++)
A> v[i] = v[i] * u[i];
A>на A>
for (int i = 0; i < v.Length; i++)
A> v[i] *= u[i];
A>Запустил оба варианта раз по десять, лучшие результаты такие: A>C# A>.NET Elapsed: 52,0000 ms A>C++ A>Elapsed = 51.3899 ms A>С++ победил!
C++ с ФВП и лямбдой дал результат сравнимый с рукопашным низкоуровневым циклом C# А теперь попробуй сравни аналогичный код на C# с ФВП и лямбдой.
Вообще, я в и той теме говорил, да уже и в этой сказал: и на C# и на Java можно писать быстрый код, который будет отставать от C++ буквально на десятки процентов (не считая удары ниже пояса типа автовекторизации/автопареллизации). Но при этом на C++ будет высокоуровневый код, а на C#, и тем более на Java — низкоуровневая возня, что ты успешно и продемонстрировал.
EP>А у меня тот код который быстрый обычно оперирует обобщёнными абстракциями — это более высокий уровень — because I can. И в таком коде я спокойно могу использовать например ФВП и замыкания, а не расписывать вручную каждую комбинацию алгоритма, контейнера и предиката — because I can.
A>>Сравнивать скорость имеет смысл у того кода, который в самом деле её требует и который пишется с оглядкой на скорость, а не на удобство, и использует все доступные в языке средства оптимизации.
EP>Это же одна из главных фишек C++ — удобно писать быстрый код. Цитата в тему: EP>
EP>"The going word at Facebook is that 'reasonably written C++ code just runs fast,' which underscores the enormous effort spent at optimizing PHP and Java code. Paradoxically, C++ code is more difficult to write than in other languages, but efficient code is a lot easier [to write in C++ than in other languages]." – Herb Sutter at //build/, quoting Andrei Alexandrescu
Точно подмечено, легко писать быстрый код и трудно весь остальной. Которого на порядки больше
Теоретически должно быть удобно писать основной код на C#/Java и вызывать функции на C++ там, где нужна чистая скорость — что-нибудь типа перемолотить большой объём данных или миллион раз перемножить несколько матриц. Но практически [у меня] получается, что удобнее в нескольких ограниченных местах снизить уровень абстракции и запачкать руки низкоуровневым кодом, чем смешивать в проекте несколько языков и таскать за управляемой сборкой пару нативных бинарников для x86/x64. Потому что дополнительная прибавка в скорости не оправдывает лишней мороки.
EP>C++ с ФВП и лямбдой дал результат сравнимый с рукопашным низкоуровневым циклом C# А теперь попробуй сравни аналогичный код на C# с ФВП и лямбдой.
С лямбдой C# показывает 81мс. С++ быстрее всего в полтора раза. Как обычно.
Здравствуйте, alexzz, Вы писали:
A>Запустил оба варианта раз по десять, лучшие результаты такие: A>C# A>.NET Elapsed: 52,0000 ms A>C++ A>Elapsed = 51.3899 ms A>С++ победил!
Что-то какие-то сомнительные результаты. Я сейчас запустил у себя прямо этот код и получил:
.NET Elapsed: 96,0000 ms
Elapsed = 38.0022 ms.
Причём если отличие второго твоего результата я ещё могу объяснить использованием какого-нибудь сомнительного компилятора C++ (типа старого msvc) или же более слабым процессором, то первый результат для меня очень удивителен, т.к. компилятор C# вроде как только один (вариант что у тебя процессор в 2 раза быстрее моего Haswell Xeon отпадает сам собой).
Здравствуйте, xRAZORx, Вы писали:
RAZ>Тут читал на хабре статью по сравнению производительности C# и C++. Сам джаву знаю плохо, но, может быть, кто-то производил замеры сам? Было бы интересно посмотреть сравнение, тем более везде говорят, что шарп и джава одинаковые по скорости.
Скажите, что принципиально нельзя написать написать на C++ но можно на java или C#?
Здравствуйте, Zenden, Вы писали:
Z>Здравствуйте, xRAZORx, Вы писали:
RAZ>>Тут читал на хабре статью по сравнению производительности C# и C++. Сам джаву знаю плохо, но, может быть, кто-то производил замеры сам? Было бы интересно посмотреть сравнение, тем более везде говорят, что шарп и джава одинаковые по скорости.
Z>Скажите, что принципиально нельзя написать написать на C++ но можно на java или C#?
На C++ можно написать java и C# и остальные языки.
И в конечном итоге все это сводится к машине Тьюринга.
Так что дело в выразительности языков и, как следствие, производительности программиста.
EP>А у меня тот код который быстрый обычно оперирует обобщёнными абстракциями — это более высокий уровень — because I can. И в таком коде я спокойно могу использовать например ФВП и замыкания, а не расписывать вручную каждую комбинацию алгоритма, контейнера и предиката — because I can.
А как написать сортировку, не расписав вручную для каждого контейнера?
Хотя я задавался таким вопросом помню, и таки да, такая сортировка существует, но от оптимальной она далека.
Так что обобщённость тоже имеет свой предел.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, alex_public, Вы писали:
_>Что-то какие-то сомнительные результаты. Я сейчас запустил у себя прямо этот код и получил: _>.NET Elapsed: 96,0000 ms _>Elapsed = 38.0022 ms.
_>Причём если отличие второго твоего результата я ещё могу объяснить использованием какого-нибудь сомнительного компилятора C++ (типа старого msvc) или же более слабым процессором, то первый результат для меня очень удивителен, т.к. компилятор C# вроде как только один (вариант что у тебя процессор в 2 раза быстрее моего Haswell Xeon отпадает сам собой).
Ну как "один"? Есть Roslyn, в котором большой упор сделан как раз на оптимизацию кода.
А тут даже непонятно под какую платформу код компилился, для x86 и x64 IL может быть в некоторых случаях сильно разным.
Здравствуйте, v6, Вы писали:
v6>Ну как "один"? Есть Roslyn, в котором большой упор сделан как раз на оптимизацию кода.
В смысле один производитель. Понятно, что могут быть разные версии, но сомневаюсь что из-за этого может быть разница в 2 раза. Тем более, что у меня тоже не совсем древняя версия стоит (хотя и не Roslyn).
v6>А тут даже непонятно под какую платформу код компилился, для x86 и x64 IL может быть в некоторых случаях сильно разным.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, alexzz, Вы писали:
A>>Запустил оба варианта раз по десять, лучшие результаты такие: A>>C# A>>.NET Elapsed: 52,0000 ms A>>C++ A>>Elapsed = 51.3899 ms A>>С++ победил!
_>Что-то какие-то сомнительные результаты. Я сейчас запустил у себя прямо этот код и получил: _>.NET Elapsed: 96,0000 ms _>Elapsed = 38.0022 ms.
Я посмотрел. У меня достаточно скомпилировать C#-пример под любую версию .Net Framework от 4 и выше (CLR > 2.0), чтобы скорость не отличалась от С++. Посмотрел дизассемблер. Если выбрать 3.5, то оператор умножения не инлайнится, а внутри него ещё есть вызов конструктора, который тоже не инлайнится. В более поздних версиях инлайнится всё.
Версию x86 особо не разглядывал, она всего на несколько миллисекунд медленнее x64. Непринципиально.
Здравствуйте, v6, Вы писали:
v6>Ну как "один"? Есть Roslyn, в котором большой упор сделан как раз на оптимизацию кода. v6>А тут даже непонятно под какую платформу код компилился, для x86 и x64 IL может быть в некоторых случаях сильно разным.
IL же на то и IL, что одинаковый на всех платфоормах. Иначе в нём смысла нет. Разницу в скорости, если она есть, делает JIT.
Здравствуйте, alexzz, Вы писали:
A>Я посмотрел. У меня достаточно скомпилировать C#-пример под любую версию .Net Framework от 4 и выше (CLR > 2.0), чтобы скорость не отличалась от С++. Посмотрел дизассемблер. Если выбрать 3.5, то оператор умножения не инлайнится, а внутри него ещё есть вызов конструктора, который тоже не инлайнится. В более поздних версиях инлайнится всё.
Внутри 4.0-4.6 происходит замена компилятора. Так что какую бы целевую не выбрать, всё равно будет использоваться один и тот же jit.
Я тут разобрался с вопросом. У меня стояла самая последняя версия .net, которая присутствует у обычных пользователей — 4.5.2 (4.6 не ставится автоапдейтами). И она выдаёт именно тот результат, что я показывал в сообщениях раньше. Сейчас я ради развлечения поставил себе .net 4.6 (скачав руками с сайта MS) и получил результат в 42 мс. Это всё объясняет и делает все цифры похожими на правду. Похоже что RyuJIT действительно работает и ускоряет более чем вдвое (на данном примере). Хотя и всё равно немного уступает нормальному компилятору C++ (там 37 мс).
P.S. Вот теперь думаю, удалить ли 4.6 с компьютера (а то ведь все тесты тогда будут соответствовать не тому, что будет на компьютерах у конечных пользователей) или оставить ради 1,5 сомнительных .net программок, установленных на компьютере.
Скомпилировал тот свой старый тест (с клеточным автоматом) с помощью .net 4.6. Так вот нормальная версия C# стала заметно медленнее (9+ секунд, вместо 8)! А unsafe версия стала заметно быстрее (4 секунды вместо 4,6). Очень забавно и непредсказуемо. Хорошо что обычных пользователей это пока почти не затрагивает (win10 — это всё же ещё экзотика).
Здравствуйте, alexzz, Вы писали:
EP>>Это же одна из главных фишек C++ — удобно писать быстрый код. Цитата в тему: EP>>
EP>>"The going word at Facebook is that 'reasonably written C++ code just runs fast,' which underscores the enormous effort spent at optimizing PHP and Java code. Paradoxically, C++ code is more difficult to write than in other languages, but efficient code is a lot easier [to write in C++ than in other languages]." – Herb Sutter at //build/, quoting Andrei Alexandrescu
A>Точно подмечено, легко писать быстрый код и трудно весь остальной.
Не трудно, а труднее. Да и основные трудности например от отсутствия модулей, зоопарка целевых платформ, исторически сложившихся шероховатостей языка и в среднем более слабой поддержкой всяких рефакторингов со стороны IDE, а не от отсутствия каких-то фич (их-то как раз много уникальных среди остального mainstream).
Причём местами даже легче — взять то же мета-программирование, оно не только про скорость, но и про удобство (например EDSL). Или та же кроссплатформенность с самым широким охватом.
A>Которого на порядки больше A>Теоретически должно быть удобно писать основной код на C#/Java и вызывать функции на C++ там, где нужна чистая скорость — что-нибудь типа перемолотить большой объём данных или миллион раз перемножить несколько матриц.
Во многих областях так и делают, например всякие инженерные дела — только там не C#/Java (Java вообще тут не к месту — самый отсталый mainstream язык), а например Python + C++.
A>Но практически [у меня] получается, что удобнее в нескольких ограниченных местах снизить уровень абстракции и запачкать руки низкоуровневым кодом, чем смешивать в проекте несколько языков и таскать за управляемой сборкой пару нативных бинарников для x86/x64. Потому что дополнительная прибавка в скорости не оправдывает лишней мороки.
Всё зависит от прикладной области, от цены тормозов, от объёма и сложности того необходимого низкоуровневого кода. Да, согласен, добавление нового языка в любой проект это такой приличный порог, который далеко не всегда преодолевается языковыми преимуществами.
EP>>C++ с ФВП и лямбдой дал результат сравнимый с рукопашным низкоуровневым циклом C# А теперь попробуй сравни аналогичный код на C# с ФВП и лямбдой. A>С лямбдой C# показывает 81мс. С++ быстрее всего в полтора раза.
Ты какой код с каким сравнивал, и на каких компиляторах? Там по ссылке есть архив с js+html, я его запускал в Firefox.
A>Как обычно.
Это всего лишь небольшой пример. Обычно слоёв абстракций больше. И в C#/Java обычно pointer semantics, в то время как в C++ value semantics — тоже приличный источник для кратной разницы.
Здравствуйте, T4r4sB, Вы писали:
EP>>А у меня тот код который быстрый обычно оперирует обобщёнными абстракциями — это более высокий уровень — because I can. И в таком коде я спокойно могу использовать например ФВП и замыкания, а не расписывать вручную каждую комбинацию алгоритма, контейнера и предиката — because I can. TB>А как написать сортировку, не расписав вручную для каждого контейнера?
Абстрагировав интерфейс доступа к элементам последовательности в отдельную концепцию, например в итераторы. Один вариант итератора соответствует множеству контейнеров.
TB>Хотя я задавался таким вопросом помню, и таки да, такая сортировка существует, но от оптимальной она далека.
С сортировкой много разных моментов связанно. Выбор оптимальной зависит не только от типа контейнера/элемента.
Например тот же связный список можно отсортировать слияниями через жонглирование связями (никак не перемещая сами элементы), а можно сначала переместить данные в массив, отсортировать интроспективной сортировкой, и переместить обратно. Оптимальный выбор зависит от ситуации, к примеру перелинковка может быть крайне нежелательна, так как может сильно нарушить последовательность узлов в памяти. Тем не менее, оба из этих вариантов сортировок могут быть реализованы обобщённо.
TB>Так что обобщённость тоже имеет свой предел.
Естественно имеет. В подобных случаях кстати отлично работает перегрузка/специализация. Каноничный пример std::advance — для RandomAccess он Θ(1), для остальных Θ(n).
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Абстрагировав интерфейс доступа к элементам последовательности в отдельную концепцию, например в итераторы. Один вариант итератора соответствует множеству контейнеров.
Да, это я понимаю, а дальше что? А дальше тебе придётся учитывать, что на каждом контейнере оптимальная сортировка — своя.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
EP>>Абстрагировав интерфейс доступа к элементам последовательности в отдельную концепцию, например в итераторы. Один вариант итератора соответствует множеству контейнеров. TB>Да, это я понимаю, а дальше что? А дальше тебе придётся учитывать, что на каждом контейнере оптимальная сортировка — своя.
Не на каждом контейнере, а на каждой концепции контейнеров/итераторов. Для std::array и std::vector оптимальная сортировка одинаковая, для safe и unsafe random access итераторов тоже одинаковая. Для разных реализаций односвязного списка — тоже одинаковая.
При этом возможность использовать самую общую сортировку для разных типов контейнеров, не теряя в скорости по сравнению с ручным расписыванием этой же сортировки для каждого контейнера — вполне себе фича.
Здравствуйте, vdimas, Вы писали:
DR>>>Встречал в сортировке по прозрачности, когда предметов в неправильном порядке мало и они идут друг за другом. N>>Сортировка вставками? V>Пузырьковая для лучшего случая однопроходная.
Для таких случаев и сортировка вставками однопроходная, плюс для многих других. Её кстати используют в реализациях std::sort на одном из финальных этапов.
Здравствуйте, alexzz, Вы писали:
v6>>Ну как "один"? Есть Roslyn, в котором большой упор сделан как раз на оптимизацию кода. v6>>А тут даже непонятно под какую платформу код компилился, для x86 и x64 IL может быть в некоторых случаях сильно разным.
A>IL же на то и IL, что одинаковый на всех платфоормах. Иначе в нём смысла нет. Разницу в скорости, если она есть, делает JIT.
Ты прав, что-то я не то написал. А вот JIT действительно может давать разницу.