Навеяно вот этой статьей.
Существует три мнения относительно производительности C++ и C#.
Те кто знают (или думают что знают) C++ считают, что C++ в разы или даже на порядки быстрее.
Те кто знают C++ и C# знают, что для обычных задач быстродействие C++ не нужно, а там где нужно — можно и C#-код заоптимизировать до невозможности. Верхний предел оптимизации у C++ выше, чем у C#, но такие рекорды никому не нужны.
Те кто знают только C# — никогда не испытывали проблем с его быстродействием.
Люди из первой категории все время пытаются доказать свою правоту. При этом приводят примеры оптимизированного кода на C++ и самого пессимизированного кода на C#.

Любой программист, который знает C# сразу увидит две ошибки:
При этом в реальности ни один C#-программист не напишет код с GC.Collect и очень малая часть программистов допустит ошибку в цикле for.
Какой смысл сравнивать гарантированного неэффективный код на C# даже с обычным кодом на C++? Разве что доказать свою точку зрения.
Чтобы сравнивать языки честно — надо сравнивать типовой код на обоих языках. То есть такой код, который можно встретить в программах с вероятностью больше статистической погрешности.
Для примера буду использовать ту же саму пузырьковую сортировку массива.
Для случая C++ протестирую три варианта:
Каждый тест будет запускаться по 100 раз и результат будет усредняться.
В тесте честно создается, заполняется, уничтожается массив и вызывается функция для его сортировки.
Код почти такой же как в первом случае, но теперь явно не освобождаем память, а отдаем все на откуп автоматической деструкции.
Кто знает C++ уже поняли, что std::array не вызывает аллокаций, а сам массив хранится в теле класса, то есть в стеке в этом примере. std::array должен стать однозначным лидером в этом забеге.
Код полностью аналогичен варианту с std::array. Но std::vector — изменяемый массив, в отличие от std::array. Поэтому vector использует динамическую память для хранения массива и должен честно проверять выход за границы.
Также сделаю три теста:
В отличие от «типичного» подхода сравнения производительности, я не буду вызывать GC.Collect, а положусь на рантайм.
Тесты будут прогоняться многократно, поэтому уборки мусора будут случаться и учитываться в замерах.
Очень важный момент — в ограничении цикла for стоит m.Length (минус константа). Такой паттерн определяется JIT и устраняет проверки на границу массива.
Сортировка выглядит так же, только используются указатели, и гарантированно никаких проверок (и никаких оптимизаций). Я не стал делать тест с fixed array, потому что в реальности его не встретишь.
В отличие от обычного массива, для List нет оптимизаций в JIT, поэтому значение длины списка храним в переменной.
Я компилировал код в Visual Studio 2015, запускал под .NET Framework 4.6. Везде настройки Release, по умолчанию.
Получил вот такие результат:
В режиме x86 оптимизатор полностью выкинул сортировку для std::array, поэтому получилось 0. В реальности работает чуть быстрее, чем C-style массив за счет отсутствия аллокаций.
Код можно найти тут — github.com/gandjustas/PerfTestCSharpVsCPP
В C# массив — ссылочный тип. Поэтому без проблем можно передавать в любую функцию. В C++ все контейнеры ведут себя как «размерные» типы и копируют все содержимое при передаче в качестве параметра. Нужно очень внимательно писать код для C++, чтобы не копировать лишний раз массивы. Зачастую придется прибегать к умным указателям, которые несут дополнительный оверхед.
В большом приложении такой оверхед сожрет весь выигрыш от использования C++. Заметно выиграть можно только на задачах с кучей математики, в которой компилятор C++ традиционно силен, или при отказе от идиом C++ и использовании голых указателей. Но последнее чревато кучей ошибок.
По просьбам читателей изменил и дополнил тесты:
Результаты получились такие:
Получается в .NET Native быстродействие такое же, как у C++.
Из-за тормозов в дебаге для std::swap отлаживать стало невозможно.
Исходники там же: github.com/gandjustas/PerfTestCSharpVsCPP
Существует три мнения относительно производительности C++ и C#.
Те кто знают (или думают что знают) C++ считают, что C++ в разы или даже на порядки быстрее.
Те кто знают C++ и C# знают, что для обычных задач быстродействие C++ не нужно, а там где нужно — можно и C#-код заоптимизировать до невозможности. Верхний предел оптимизации у C++ выше, чем у C#, но такие рекорды никому не нужны.
Те кто знают только C# — никогда не испытывали проблем с его быстродействием.
Люди из первой категории все время пытаются доказать свою правоту. При этом приводят примеры оптимизированного кода на C++ и самого пессимизированного кода на C#.
Пример «типичного» сравнения

Любой программист, который знает C# сразу увидит две ошибки:
- Вызов GC.Collect, который сводит на нет любые оптимизации сделанные в рантайме для сборки мусора.
- Использование for цикла, который гарантированно не устраняет проверки границ на каждое обращение к массиву.
При этом в реальности ни один C#-программист не напишет код с GC.Collect и очень малая часть программистов допустит ошибку в цикле for.
Какой смысл сравнивать гарантированного неэффективный код на C# даже с обычным кодом на C++? Разве что доказать свою точку зрения.
Честное сравнение
Чтобы сравнивать языки честно — надо сравнивать типовой код на обоих языках. То есть такой код, который можно встретить в программах с вероятностью больше статистической погрешности.
Для примера буду использовать ту же саму пузырьковую сортировку массива.
Тесты для C++
Для случая C++ протестирую три варианта:
- С-style массив (указатель)
- std::array
- std::vector
Каждый тест будет запускаться по 100 раз и результат будет усредняться.
Код измерения
std::chrono::high_resolution_clock::duration measure(std::function<void()> f, int n = 100)
{
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < n; i++)
{
f();
}
auto end = std::chrono::high_resolution_clock::now();
return (end - begin) / n;
}
Код теста для C-style
Код
void c_style_sort(int *m, int n)
{
for (int i = 0; i < N - 1; i++)
for (int j = i + 1; j < N; j++) {
if (m[i] < m[j])
{
int tmp = m[i];
m[i] = m[j];
m[j] = tmp;
}
}
}
void c_style_test()
{
int* m = new int[N];
for (int i = 0; i < N; i++)
{
m[i] = i;
}
c_style_sort(m, N);
delete[] m;
}
В тесте честно создается, заполняется, уничтожается массив и вызывается функция для его сортировки.
Код теста для std::array
Код
void cpp_style_sort(std::array<int, N> &m)
{
auto n = m.size();
for (int i = 0; i < n-1; i++)
for (int j = i + 1; j < n; j++) {
if (m[i] < m[j])
{
int tmp = m[i];
m[i] = m[j];
m[j] = tmp;
}
}
}
void cpp_style_test()
{
std::array<int, N> m;
for (int i = 0; i < N; i++)
{
m[i] = i;
}
cpp_style_sort(m);
}
Код почти такой же как в первом случае, но теперь явно не освобождаем память, а отдаем все на откуп автоматической деструкции.
Кто знает C++ уже поняли, что std::array не вызывает аллокаций, а сам массив хранится в теле класса, то есть в стеке в этом примере. std::array должен стать однозначным лидером в этом забеге.
Код теста для std::vector
Код
void vector_sort(std::vector<int> &m)
{
auto n = m.size();
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++) {
if (m[i] < m[j])
{
int tmp = m[i];
m[i] = m[j];
m[j] = tmp;
}
}
}
void vector_test()
{
std::vector<int> m;
m.reserve(N);
for (int i = 0; i < N; i++)
{
m.push_back(i);
}
vector_sort(m);
}
Код полностью аналогичен варианту с std::array. Но std::vector — изменяемый массив, в отличие от std::array. Поэтому vector использует динамическую память для хранения массива и должен честно проверять выход за границы.
Тесты для C#
Также сделаю три теста:
- Обычный массив
- Обычный массив с использованием unsafe (указателей)
- System.Collections.Generic.List
В отличие от «типичного» подхода сравнения производительности, я не буду вызывать GC.Collect, а положусь на рантайм.
Тесты будут прогоняться многократно, поэтому уборки мусора будут случаться и учитываться в замерах.
Код измерения
static long Measure(Action f, int n = 100)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
f();
}
return sw.ElapsedMilliseconds / n;
}
Код теста для обычного массива
Код
static void ArrayTest()
{
var m = new int[N];
for (int i = 0; i < m.Length; i++)
{
m[i] = i;
}
ArraySort(m);
}
static void ArraySort(int[] m)
{
for (int i = 0; i < m.Length - 1; i++)
for (int j = i + 1; j < m.Length; j++)
{
if (m[i] < m[j])
{
int tmp = m[i];
m[i] = m[j];
m[j] = tmp;
}
}
}
Очень важный момент — в ограничении цикла for стоит m.Length (минус константа). Такой паттерн определяется JIT и устраняет проверки на границу массива.
Код теста для unsafe
Код
static unsafe void UnsafeTest()
{
var m = new int[N];
fixed(int* ptr = &m[0])
{
for (int i = 0; i < N; i++)
{
ptr[i] = i;
}
UnsafeSort(ptr, N);
}
}
static unsafe void UnsafeSort(int* m, int n)
{
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
{
if (m[i] < m[j])
{
int tmp = m[i];
m[i] = m[j];
m[j] = tmp;
}
}
}
Сортировка выглядит так же, только используются указатели, и гарантированно никаких проверок (и никаких оптимизаций). Я не стал делать тест с fixed array, потому что в реальности его не встретишь.
Код теста для List
Код
static void ListTest()
{
var m = new List<int>(N);
for (int i = 0; i < N; i++)
{
m.Add(i);
}
ListSort(m);
}
static void ListSort(List<int> m)
{
var n = m.Count;
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
{
if (m[i] < m[j])
{
int tmp = m[i];
m[i] = m[j];
m[j] = tmp;
}
}
}
В отличие от обычного массива, для List нет оптимизаций в JIT, поэтому значение длины списка храним в переменной.
Результаты
Я компилировал код в Visual Studio 2015, запускал под .NET Framework 4.6. Везде настройки Release, по умолчанию.
Получил вот такие результат:
Тест | x86 | x64 |
---|---|---|
(С++) С-style | 55ms | 55ms |
(С++) std::array | 0ms (52ms) | 65ms |
(С++) std::vector | 100ms | 65ms |
(C#) массив | 67ms | 90ms |
(C#) unsafe массив | 63ms | 105ms |
(C#) List | 395ms | 390ms |
В режиме x86 оптимизатор полностью выкинул сортировку для std::array, поэтому получилось 0. В реальности работает чуть быстрее, чем C-style массив за счет отсутствия аллокаций.
Выводы
- Для обоих языков идиоматичный код — самый эффективный (вообще странно если бы это было не так)
- C# медленнее C++ в таких задачах на 20%-50% (это верхняя граница)
- Для x64 оптимизировать надо отдельно (очевидно, но все же)
Код можно найти тут — github.com/gandjustas/PerfTestCSharpVsCPP
Что осталось «за кадром»
В C# массив — ссылочный тип. Поэтому без проблем можно передавать в любую функцию. В C++ все контейнеры ведут себя как «размерные» типы и копируют все содержимое при передаче в качестве параметра. Нужно очень внимательно писать код для C++, чтобы не копировать лишний раз массивы. Зачастую придется прибегать к умным указателям, которые несут дополнительный оверхед.
В большом приложении такой оверхед сожрет весь выигрыш от использования C++. Заметно выиграть можно только на задачах с кучей математики, в которой компилятор C++ традиционно силен, или при отказе от идиом C++ и использовании голых указателей. Но последнее чревато кучей ошибок.
Update
По просьбам читателей изменил и дополнил тесты:
- Использовал std::swap вместо ручного обмена в C++ коде
- Сделал вызов .at для вектора вместо [], чтобы он проверял границы в релизной сборке
- Добавил проект с .NET Native
Результаты получились такие:
Тест | x86 | x64 |
---|---|---|
(С++) С-style | 60ms | 52ms |
(С++) std::array | 51ms | 60ms |
(С++) std::vector | 147ms | 81ms |
(C#) массив | 67ms | 90ms |
(C#) unsafe массив | 63ms | 105ms |
(C#) List | 395ms | 390ms |
(C# + .NET Native) массив | 62ms | 59ms |
(C# + .NET Native) unsafe массив | 63ms | 52ms |
(C# + .NET Native) List | 274ms | 282ms |
Получается в .NET Native быстродействие такое же, как у C++.
Из-за тормозов в дебаге для std::swap отлаживать стало невозможно.
Исходники там же: github.com/gandjustas/PerfTestCSharpVsCPP
Wedmer
У меня есть подозрение, что новичкам следует изучать эту проблематику, а у большинства опытных разработчиков проблем с передачей по ссылке или указателем нет.
gandjustas
Да-да, а еще у опытных разработчиков рабочих день из 30 часов и они цитируют Кнута наизусть в обратном порядке.
Тем не менее все люди и часто ошибаются (чаще чем кажется). А в C++ легко забыть написать передачу по ссылке и получите копию. Причем в некоторых случаях это даже логику программы не поменяет. А если помножить подобные проблемы на несколько типов уминых указателей, rvalue ссылки итп, то можно очень часто ошибаться.
maaGames
Достаточно пару сотен раз обжечься и проблема забытой ссылки пропадает сама собой. Со мной в последний раз это случилось четыре года назад. Вернее, шесть лет назад, но два года эта ошибка не проявляла себя. Вектор передавался по значению, но почти всегда был из малого числа элементов и на производительности заметно не сказывалось. Лишь когда при определённом использовании размер увеличился до нескольких сотен элементов и производительность экспоненциально падать начинала (столь глупая ошибка была в рекурсивной функции), то косяк обнаружился. И сразу же был исправлен.
Столь громко осуждаемые и обсуждаемые ошибки с указателями, ссылками и прочим — по большей части удел студентов. Спустя несколько лет практики их уже на уровне инстинктов не допускаешь.А ещё помогает RAII и следование codestyle.
По поводу оверхеда умных указателей — в шарпе-то этот оверхед есть всегда и везде (unboxed не учитываем), а в Сишке только там, где пожелает программист.
gandjustas
Без комментариев.
Эта пара сотен раз во сколько сотен тысяч долларов выльется?
Katasonov
Обычно пару сотен раз обжигаются быстро и в универе.
gandjustas
В универе не учат тому, что потом придется использовать в работе. Увы.
JediPhilosopher
Ну вот конкретно примеру со ссылками меня как раз научили в универе. Я тогда слабовато знал С++, а нам устроили контрольную по алгоритмам в виде олимпиадного контеста: надо было писать код решающий задачи, отправлять его на сервер где он прогонялся на тестах, при этом были ограничения на время выполнения и на потребляемую память.
Одна задача очень долго не давалась, постоянно лимит времени превышала. Я долго там бился с оптимизацией алгоритма, а потом мне подсказали добавить & в параметр функции где я вектор передавал. И о чудо, все заработало быстрее в десятки раз. Меня это тогда так впечатлило, что запомнил на всю жизнь.
Так что универы тоже разные бывают.
gandjustas
Прекрасный пример. В C# такой проблемы вообще бы не случилось.
lasalas
Зато в C# можно наколоться с boxing/unboxing, хотя масштаб разрушений обычно меньше.
koshak
З — Зависть
0xd34df00d
Так надо заниматься программированием самостоятельно вне универского учебного плана.
Во время обучения в универе просто это делать проще и разумнее, чем уже потом на работе.
Mrrl
В универе приходится отвлекаться на учёбу :(
0xd34df00d
Таки учёбу и работу над своими хоббями можно вполне успешно совмещать. Всё ж учёба не 16 часов в сутки сжирает.
FiresShadow
[Ирония]А ещё достаточно 10-20 лет пописать на ассемблере, и проблема сложности читаемости и написания кода на ассемблере отпадает сама собой. Главное следовать codestyle. А ещё код на ассемблере можно оптимизировать лучше, чем код на С++.
Чудаки, придумали тут несуществующую проблему. Терпенье и труд все перетрут! Долой автоматизацию труда программиста! Даёшь ассемблер! [/Ирония]
FiresShadow
[Ирония]Чудная это обезьяна! Вместо того, чтобы по деревьям лазать, сбивает плоды палкой-копалкой. Ленивая какая-то. Сильные обезьяны так не делают! Достаточно пару сотен раз слазить на дерево, мышцы накачаются, и это не будет уже проблемой. Последний раз у меня были сложности с залезанием на дерево шесть лет назад. Терпенье и труд все перетрут! Долой автоматизацию труда обезьяны![/Ирония]
maaGames
Без иронии.
Чудная обезьяна целую неделю страдала-голодала, но продолжала бросать палку-копалку в пробегающих мимо антилоп. Другие обезьяны над ней смеялись, ведь палка-копалка нужна, чтобы сбивать бананы, а не антилоп. А она продолжала бросать, пока не научилась попадать в цель и жрать питательное мясо. А другие обезьяны продолжали над ней смеяться и стали зваться вегетерианцами.
Вот вы реально проблемы на пустом месте придумываете. Студенты косячат и ругают указатели и прочее, но это совсем не проблема. Опытный программист просто физически не сможет написать код с вышеозвученными проблемами, потому что подсознание спать не даст, пока не исправишь эту ошибку. Про codestyle я написал не ради красивого словца, а по делу. Если чётко сказано, что входные параметры должны быть ко константной ссылке, а выходные по ссылке или указателю, то косяк просто не пройдёт code review. Не говоря о том, что в блокнотах кодят только обезьяны и любая современная IDE обнаруживает большинство ошибок (вы ведь читаете предупреждения компилятора?), и ещё больше ошибок отлавливаются специальными утилитами (блюющим единорогом) и профайлером.
Но уровень вхождения в C# несколько ниже, с этим соглашусь. Можно говнокодить не отстреливая ноги из верёвки.
FiresShadow
Необходимость работать с памятью вручную нагружает программиста дополнительной работой. Нужно написать деструктор, конструктор копирования (без него деструктор и копирование будут работать некорректно), следить что delete вызывается там где нужно, следить передаются у вас аргументы в функцию по ссылке или через копирование в стек и т.п.
Вы более шести лет этим занимались, и поэтому вам этот рутинный труд кажется чем-то само-собой разумеющимся. Ну и эффективность вашего труда вас тоже не сильно беспокоит.
То, что нужно вручную делать монотонный труд, который можно легко автоматизировать — это проблема. Иное дело, что иногда в силу обстоятельств нужно закрыть глаза на эту проблему, потому что по-другому сделать не получится. Но от этого неудобство не перестаёт быть неудобством.
То, что вы в течении шести лет писали конструкторы копирования, не даёт вам права называть студентами и второсортными программистами тех, кто не хочет заниматься их написанием. Ну если не занимается человек микроконтроллерами, то зачем ему выполнять этот рутинный труд? Ради выигрыша в 10%-20% в скорости из-за того, что в С++ нет всевозможных проверок на выход за границы массива (не забываем про JIT-компилятор)? Нет, спасибо, я предпочитаю наличие таких проверок на уровне языка.
maaGames
Нет почти по всем озвученным тезисам.
Начну с конца.
Я не называл студентами и второстепенными тех, кто не хочет писать конструкторы, я называл студентами и второстепенными программистами тех, кто не может поставить символ & и делает из этого трагедию. Впрочем, я не называл никого второстепенными и студентами называл не в обидном смысле слова, а в смысле временного отсутствия опыта.
Конструкторы и деструкторы нужны не только и не столько для освобождения памяти. Скажу больше, сейчас у меня в проекте около полутора тысяч файлов, при этом new и delete используются буквально в десятке из них. Вызывать вручную delete? Ну написал его один раз в деструкторе — руки не отвалятся.
Если не выделять память через malloc/new, а использовать контейнеры, то писать конструкторы копирования(а ещё и присваивания!) не нужно. Если следовать RAII, то и деструкторы не всегда нужны, по крайней мере delete в них писать точно не придётся.
Выход за границы массива проверяются в дебаге и в релизном коде уже в принципе не должно быть выхода за границы (я метатель). При желании, можно проверять выход за границы массива и в релизном коде (at вместо []). Это С++, тут можно практически всё, было бы желание.
В современном программировании под «ручным управлением памяти» подразумевается, что программист в любой момент времени знает, существует ли объект или нет и знает, когда именно этот объект будет уничтожен. Может сам выбрать для этого момент, чтобы был наименьший удар по производительности. Может вообще не разрушать объект. А может положиться на RAII и вообще быть не в курсе, что такое ручное управление.
FiresShadow
На эту тему я уже высказался. Можно лазать по деревьям и гордиться своим умением не наступать на тонкие ветки и не будить спящих на дереве змей, а можно сбить фрукт палкой-копалкой. Вы же почему то высказываетесь против палки-копалки. Ваша позиция: «Будь мужиком — лезь на дерево. Не умеешь лазать по дереву — не мужик». Тут абсолютно неважно, умеет ли человек, пользующийся палкой-копалкой, не будить спящих змей на дереве. Важно, что вы выступаете против прогресса.
Не спорю, в редких ситуациях умение лазать по дереву может оказаться очень кстати. Но это не повод каждый раз лезть на дерево и заниматься рутинным трудом, когда этого можно и не делать. Просто признайте, что вам лень изучать принцип работы палки-копалки и занимайтесь вашим рутинным трудом дальше на здоровье. Зачем тормозить развитие других соплеменников? Если человек не хочет заниматься рутинным трудом, а хочет уделять больше времени более творческим задачам, и современные технологии (в том числе быстродействие современных компов) ему это позволяют, то почему бы и нет???
FiresShadow
Добавлю, что в редких случаях и знание машинных кодов нужно. Но это не оправдывает позицию: «Будь мужиком — пиши на машинных кодах. Тот, кто не знает машинных кодов на уровне подсознания — не мужик.»
maaGames
Я себя ни в чём не ограничиваю! Благодаря stl и прочим контейнерам отпадает необходимость в ручном выделении/удалении памяти для коллекций. Штучные объекты создаются менеджерами объектов(как раз те редкие файлы с new/delete), но это сделано не ради отказа от new, а из-за фабрик полиморфных объектов. Кстати, есть множественное и пирамидальное наследование, но проблем с ним тоже нет. Это я вспомнил ещё один популярный камень в огороде С++.
FiresShadow
То есть вы не делаете дополнительных действий, которых можно было бы избежать, если бы использовался сборщик мусора и ваши классы всегда передавались в функцию по ссылке?
Дестркуторов не пишете? Код дополнительно взглядом не окидываете в поисках ошибок работы с памятью? Специальные анализаторы кода периодически не запускаете? И в куче при этом объекты создаёте? Вот что-то не верится.
maaGames
Деструкторы нужны НЕ для того, чтобы вызвать в них delete. Например, сокеты я тоже в деструкторах закрываю. И с файлов блокировку снимаю. И с тех же мьютексов. Я не знаю, есть ли в C# деструкторы, если нет, то пичалька, придётся каждый раз в блок finally писать код, который можно один раз в деструктор написать.
Мне не нужно ВСЕГДА передавать классы по ссылке, потому что в некоторых случаях эффективнее по значению. Что? В шарпике тоже можно по значению передавать, но только структуры? А вот в плюсиках можно передавать так, как хочешь. А ещё можно передавать по значению, но при этом копирования объекта не будет, потому что изначально будет использоваться тот объект, который потом вернётся из функции. Да, С++ и так может, потому и компилируется код долго, что он много чего может.
Я окидываю код взглядом сотни, а то и тысячи раз. Не ради ошибок работы с памятью — этот класс ошибок у меня практически устранён на уровне архитектуры приложений. Я в принципе стараюсь не писать такой код, в котором могут быть ошибки работы с памятью. То же касается и ошибок переполнения буфера.
Анализаторы кода запускаю раз-два в месяц (не считая того анализа, на который способен компилятор при каждой компиляции). Логические ошибки обнаруживаются периодически. Неправильная инициализация обнаруживается. Дублирующиеся и не эффективные сравнения обнаруживаются. С памятью пока ничего связанного не обнаруживалось.
У Макконелла по этому поводу очень хорошо было написано: Выделил память, сразу же напиши код её освобождения. Всё проблема решена.
lair
JFYI: в C# есть и деструкторы, и детерминированное освобождение ресурсов.
maaGames
Как там может быть детерминированное освобождение ресурсов, если там сборщик мусора?
Про деструкторы — спасибо. Теперь вдвойне забавно выглядят крики о нудном создании деструкторов в плюсах.
FiresShadow
Финализаторы в С# не занимаются непосредственным освобождением памяти в куче. Да и используют их довольно редко.
maaGames
> А по вашему закрытие файлов и соединений (connection) можно сборщику мусора доверить?
Не вижу связи между закрытием соединения и освобождением памяти. И вообще, закрытие сокетов или файлов я уже использовал в качеств аргумента для создания деструктора в С++ — не засчитано. Своё придумывайте.)
lair
Да легко, типичное кооперативное поведение. Сначала объявим класс, заинтересованный в детерминированном освобождении:
Теперь используем объект такого класса (при условии, что мы хотим детерминированного поведения):
Прелесть в том, что как только мы покинули
using
(причем покинули любым способом — просто вышли за границу, сделалиreturn
, бросили exception), компилятор C# гарантирует вызов методаDispose
. Вторая прелесть в том, что если мы при этом используем только управляемые ресурсы, то если вызоваDispose
не произойдет (например, пользователю класса не важное детерминированное поведение), ресурсы все равно будут собраны, просто позже.Я просто подозреваю, что семантика деструкторов в C# и C++ отличается. В C# дескриптор — точнее, финализатор — нужен исключительно редко, и обычно это случается при взаимодействии с неуправляемыми ресурсами. Я за свою жизнь их написал явно меньше десятка.
maaGames
Например, деструктор может делать то же самое, что Dispose, без необходимости наследоваться от IDisposable. Я там оговорился и вместо «освобождение памяти» написал «освобождение ресурсов». В контексте сборки мусора и управления памятью речь шла о памяти, а не о ресурсах вообще. GC сработает так, как ему приспичет и тогда, когда ему приспичет. В С++ момент освобождения памяти детерменирован, если не используется сборщик мусора.
lair
Ну, если вам очень надо детерминированное освобождение памяти, то есть
GC.Collect()
. Другое дело, что не надо этого в .net делать, не идиоматично это.Я говорил именно о ресурсах.
0xd34df00d
Я тут рискну высказать такую крамольную мысль, с которой уже как-то тут не соглашались, но тем не менее.
Вопрос в определении детерминированности. Я настаиваю, что имеет смысл рассматривать, гм, локальную детерминированность — это когда при взгляде на достаточно мелкий кусок кода можно делать о нём выводы, и с этой точки зрения shared_ptr'ы не сильно лучше.
Поглядите на этот кусок кода и скажите, умрёт
foo
в конце функции или нет.maaGames
В данном случае ответ однозначно нет (передан по ссылке, косяк в примере). А вот умрёт ли он после возврата из функции сказать сложнее, для этого нужно глянуть на счётчик ссылок в указателе.
О детерминированности. Умный указатель (объект, на который он ссылается, если точнее) будет разрушен в тот момент, когда счётчик ссылок обнулится. Детерменированно? Абсолютно. Локальная переменная будет разрушена при выходе из области видимости(RAII) или при вызове delete. Тоже абсолютно детерменированно. При использовании сборщика мусора можно определённо сказать лишь то, что пока объектом пользуются — он точно существует. И может существовать какое-то время после того, как им уже никто не пользуется.
0xd34df00d
Скорее в формулировке, вы в следующей фразе ответили именно на тот вопрос, который я имел ввиду.
Разве сборщики мусора недетерминированны? Я не крупный специалист в этой теме, но, если я правильно всё понимаю, то при одинаковом ходе выполнения программы в одинаковых условиях (при равном объёме доступной системной памяти, например) работа GC разве не будет также одинаковой?
Ну и толку с этой детерминированности, если она разумно и локально размышлять о программе и её поведении всё равно не даёт зачастую, как в примере выше, а размышлять нелокально неудобно: у людей attention span маленький, а для машин там какой-нибудь комбинаторный взрыв числа вариантов control flow пойдёт. Не панацея, в общем, как бы мне, как любителю и поклоннику C++, не было печально.
FiresShadow
А если в конструкторе был выделен массив в куче, то где прикажете его освобождать? Отдельную функцию завести? И в чём выгода, почему не в деструкторе?
Что? Передача по значению происходит путём копирования полей объекта в стек. Если изменение переданного по значению параметра приводит к изменению исходного объекта, значит одно из полей объекта — указатель, и вы изменили память по этому указателю. Но даже этот указатель копируется. Если вы в функции этому указателю присвоите новый адрес, то в исходном объекте адрес указателя не изменится.
Не сомневаюсь, что столь опытный программист, коим вы себя позиционируете, сможет справиться с этой проблемой, не занимаясь дублированием кода.
Вы уверены, что дело в богатстве возможностей языка, а не в компиляторе? Кстати, C# тоже умеет много чего, чего не умеет С++: рефлексия, динамическая кодогенерация, динамик прокси (на основе кодогенерации), мок-объекты (на основе динамик-прокси)
maaGames
> А если в конструкторе был выделен массив в куче
vector и никаких куч
> Передача по значению происходит путём копирования полей объекта в стек.
С++11 и конструктор перемещения. Если компилятор сочтёт эффективным, то вместо копирования будет редактирование того объекта, который будет возвращён из функции, а не объекта, который будет затем скопирован.
> что столь опытный программист, коим вы себя позиционируете
Я ни слова не говорил о своём абсолютном опыте, а лишь относительно автора статьи. Например, С++14 я вообще не знаю. C# я вообще почти не знаю, поэтому без деструктора варианта кроме finally не вижу (мне уже сказали, что деструктор есть, всё ОК).
> C# тоже умеет много чего, чего не умеет С++
С++ тоже это всё умеет. Только это придётся самому запрограммировать…
FiresShadow
У вектора есть деструктор. И вектор хранит свои элементы в куче.
Стандартное средство борьбы с дублированием кода — вынесение дублирующегося фрагмента в отдельную сущность. От языка практически не зависит.
И как это вы интересно будете делать рефлексию? Проанализируете программой собственные бинарные коды, взятые из оперативки? Во-первых, тут уже используется не столько С++, сколько знание ассемблера и машинных кодов. Во-вторых, даже зная особенности работы всех сишных компиляторов, вы вряд ли всегда сможете однозначно обнаружить все классы и их методы — из-за оптимизации, обфускации, ассемблерных вставок, возможности создания нового компилятора и т.п.
Door
Это если по-умолчанию, передаём что-то типа вот такого аллокатора — и всё на стеке
maaGames
> У вектора есть деструктор. И вектор хранит свои элементы в куче.
В управляемых языках тоже есть и куча и стек и регистры процессора, но речь же о ручном освобождении памяти.
> вынесение дублирующегося фрагмента в отдельную сущность
И ручное прописывание этой функции в finally, что то же самое дублирование, только в меньшем масштабе. Мне уже ответили, что в шарпе есть деструкторы, так что этот пункт с повестки снимается.
> И как это вы интересно будете делать рефлексию?
> научите рефлексии без препроцессора (это из коммента ниже, но я только раз в 5 минут писать могу, так что тут отвечу)
Почему в Шарпе можно дописывать аттрибуты и свойства и всякое разное, чтобы включить поддержку какой-нибудь рефлексии, а плюсы должны быть прям по учебнику и даже без препроцессинга? На С/С++ можно запрограммировать практически всё что угодно, вопрос только в велосипедах и костылях. Раз в С++ до сих пор нет рефлексии, значит, она не настолько востребована в высокопроизводительных приложениях. Либо её нельзя эффективно реализовать в принципе. Я не пользовался рефлексией в других языках, так что не знаю обо всех сферах её применения.
С другой стороны, чем словарь из строк и функторов не рефлексия? Типа map<string,functor)? Повторюсь, я не знаю, что такое рефлексия и описание из википедии похоже именно на добавление/подмену методов класса.
lair
Подозреваю, что вам все-таки лучше сначала узнать, что такое reflection, и как он устроен в .net, прежде, чем спорить на эту тему. Ничего «дописывать» не надо, рефлексия работает всегда и доступна из коробки.
Тут возникает разумный вопрос: а «высокопроизводительные приложения» — это единственная область применения С++?
Если вкратце, то reflection (по крайней мере, в .net) — это способность в рантайме получать метаданные кода (как выполняемого, так и нет), и потом выполнять код на основании этих метаданных. Типичный сценарий — имея на входе класс (не объект), получить список всех свойств, определенных в этом классе, затем сопоставить их с входными данными (скажем, xml-файлом), и создать объект этого класса, где свойства будут заполнены входными данными.
maaGames
Да, без подключения интерпретатора или кодогенерации вряд ли получится в плюсах рефлексию использовать. Но если только данные(свойства), без поведения, то хоть на Сишке можно сделать. Разумеется, будет не так красиво, как на шарпе.
0xd34df00d
Серьёзно, пожалуйста, научите рефлексии без препроцессора до принятия соответствующего пропозала.
Лучшее, к чему я пришёл на данный момент — это что-то вроде такого, это чтобы не писать ручками SQL для операций со структуркой Record. ORM этакий прям.
Не было бы необходимости поддерживать boost 1.57, можно было бы взять форму
BOOST_FUSION_ADAPT_STRUCT
из 1.58 без указания типов полей.Ну и выглядит стрёмно это всё.
Pushkoff
я правильно понимаю, вы весь этот код написали ради SetAvatar/GetAvatar?
а вы не думали что даже прямое использование, довольно примитивного SQLite API в этих функциях займет меньшее число строк кода, чем у вас?
P.S. отсутствие рефлексии в С++ конечно расстраивает…
0xd34df00d
Почему же меньшее? Надо как минимум написать будет руками все SQL-запросы, а лучше их вынести в ресурсы и подгружать оттуда, следить за этим, модифицировать систему сборки…
Нет, я лучше напишу
BOOST_FUSION_ADAPT_STRUCT
, и всё. Ну и ещё пару трейтов специализирую, если используются какие-то особые типы данных (вроде перечисления дляSize
). А запросы все за меня сгенерируются, включая какие-нибудь хитрые селекты по разным подмножествам полей.Door
кхм, как вы умудряетесь так много и постоянно комитить )?
JIghtuse
Напишите свой Leechcraft — поймёте =)
0xd34df00d
Писать код люблю просто :)
bigfatbrowncat
.У меня есть очаровательный пример. Не могу сказать, чтобы я был крутым спецом в C++, но «пару сотен раз обжечься» со мной случалось. Припомню лишь один такой случай (один из последних).
Я писал программу из нескольких десятков классов. Писал довольно аккуратно, с полиморфизмом и строгой инкапсуляцией. Программа была пользовательская, графическая (игра). И, каюсь, юнит-тестированием я пренебрег по неопытности.
В один не очень прекрасный момент я обнаружил, что программа моя падает при завершении. Пока работает — всё окей. А на выходе «на посошок» — SEGFAULT. Думаю, все с таким сталкивались.
Долго я ломал голову, в чем дело. На что только не думал. Все деструкторы по 20 раз проверил, брейкпоинтов понаставил… Всё тщетно. Решение обнаружилось при внимательном перечитывании кода, к которому я пришел от отчаяния. Один объект получал в конструкторе ссылку на другой и тут же ее сохранял. То есть, конструкция такого вида:
Оба объекта были крупными модулями логики программы. Ошибка состояла в том, что я прозевал и не написал амперсанд.
И, не говоря мне ни слова, компилятор честно скопировал объект
b
. Тот был для копирования не предназначен и при деструкции я получил ошибку двукратного освобождения памяти (которая под MINGW, почему-то, обратилась SEGFAULT-ом).Сейчас я, конечно, понимаю, что в классе B можно было запретить копирование, создав неимплементированный оператор присвоения и копирующий конструктор (ибо он действительно был не предназначен для копирования). И, конечно, имей я чуть больше опыта, я бы, в первую очередь посмотрел именно в сторону ссылок, а не деструкторов.
Всё это справедливо. Я своей глупости не отрицаю.
Но есть и другая правда. А именно: язык, в котором один забытый символ в типично используемой конструкции приводит к появлению трудновылавливаемой ошибки времени исполнения с молчаливого одобрения компилятора — плохой язык. Он спроектирован из расчета на непогрешимость разработчика. А разработчик — человек, ему свойственно ошибаться и платить головой (или целым днем работы) за мелкую опечатку — совсем не комильфо.
Я навскидку предложу 2 варианта решения проблемы, которые, почему-то, не пришли на ум господину Страуструпу (или он, почему-то, не счел их приемлимыми).
1. Запретить «неявный» копирующий конструктор для объектов и то же самое — с оператором присвоения. Хотя бы заставить разработчика явно написать «разрешаю копирование» в декларации класса, чтобы исключить «недоразумение по умолчанию». Я бы не поленился пару слов написать — это быстрее, чем полдня дебажить.
2. (Прекрасное решение, реализованное в языке C#) Поделить все объекты строго на две категории — те, которые всегда передаются только по ссылке и те, которые передаются исключительно по значению. В 99% случаев никому не нужно один и тот же объект передавать и так, и так. Для остального 1% нетрудно создать метод «clone» или что-то в этом духе.
И таких примеров много. C++ плох тем, что он, помогая разработчику писать код, не мешает ему делать серьезные ошибки. Он не защищает от ситуации, когда ошибка в одном модуле приводит к падению в совсем другом. Иногда проще переписать программу с нуля, чем поймать случайный memory corruption crash…
Wedmer
По сравнению с C, в ногу таки выстрелить сложнее. Но если вам это удастся, то попрощайтесь со всей ногой.
То, что не предполагает копирования, всегда должно иметь соответствующую защиту. Если вы не сделали этого, то это ваш косяк, а не языка. Если вы в армии кого либо подстрелите, забыв поставить оружие на предохранитель, то виноваты все таки будете вы, а не оружие.
bigfatbrowncat
В Си нет ООП, сравнение некорректно.
Я еще раз повторю — я свой косяк не отрицаю. Но вот в остальном я с вашей логикой совершенно несогласен:
В языке C++ — именно так. В нормальном ЯП — совершенно неверное утверждение. Я не был в армии, но полагаю, что когда (если) личному составу выдается заранее заряженное оружие, предохранитель должен быть выставлен по умолчанию. Спуск можно нажать случайно. Можно просто уронить оружие. Мало ли, какие случайности бывают…
В языке Паскаль, например, по умолчанию был выставлен range checking. Да, его можно отключить, но если ты это сделал, то либо ты знаешь, что творишь, либо — «сам себе злобный Буратино». А когда у меня вся панель управления забита «красными кнопками» без защиты, то это — не боевая машина для профессионалов. Это — минное поле.
А теперь — без метафор. Любая система, предназначенная для использования людьми, должна быть спроектирована так, чтобы повышение опасностей и рисков (а не понижение их) достигалось дополнительными усилиями. В областях инженерии, где риск носит фатальный характер, это давно усвоили. Например, на заводах в цехах штамповки и резки станок имеет не одну спусковую кнопку, а две. И расположены они в разных его концах, а нажимать их надо ладонями, одновременно. Как считаете, там много работает идиотов, которые не знают, что руки под нож совать не следует? И тем не менее, одного «понимания» этой банальности работниками инженерам показалось недостаточно — они специально сделали так, чтобы руки были заняты в момент удара.
В Java есть миллион способов убить программу неопределяемой ошибкой. Но для этого надо как минимум задействовать рефлексию. А лучше — JNI. Еще лучше — динамическую кодогенерацию на JVM. И объем кода, на котором это удастся сделать — тысяча классов. На меньшем вы с легкостью найдете ошибку.
А если мне надо высокую производительность, я напишу кусок функционального кода на C и сделаю JNI-интерфейс. Это — тоже риск, но там проблем меньше. Да и редко это бывает необходимо, откровенно говоря.
Я честно старался полюбить C++. Изучал его несколько лет (параллельно с C# и Java). Писал что-то на нем. Но всякий раз я втыкался в совершенно непредсказуемые результаты совершенно, на первый взгляд, безобидных действий.
Wedmer
Я вас поздравляю, вы подстрелили инструктора на стрельбище и угодили под военный трибунал. Заранее заряженное оружие не дают, вы его сами заряжаете. Так что оправдания вроде «а чего он сам на предохранитель не поставился?» на трибунале не пройдут.
bigfatbrowncat
Не все люди пригодны к военной службе. Из моих знакомых, занимающихся наукой, программированием и прочими интеллектуальными дисциплинами в армии не служил никто, а военные сборы посещали единицы.
Я предпочитаю пользоваться инструментами, которые помогают мне в работе, а не дергают за нервы. Я могу писать на C++. Просто он мне неприятен. И аргументов хватит на целую статью, просто писать ее смысла нет. Одни люди со мной и так согласны (многие из них знают этот язык лучше меня), а другие (в частности, вы), к подобным аргументам не прислушиваетесь. Я высказал лишь самое вопиющее.
Wedmer
Тогда приведите пример инструмента, который так же гибок, быстр, дает работать напрямую с памятью, создает код почти для любой платформы, но не имеет минусов C++.
bigfatbrowncat
Последние два года я разрабатываю проекты, включающие в себя 2 слоя. Нижний уровень пишется платформонезависимым способом на языке Си. Это обычно высокоскоростной слой, который занимается взаимодействием с графикой или чем-то подобным. Дальше идет JNI-интерфейс. Верхний слой пишется на Java. Там находится вся логика, там если и приходится думать об управлении ресурсами, то только в рамках имплементации интерфейса Disposable.
Взаимодействие с ОС осуществляется через JRE, поэтому проект получается кроссплатформенный.
При этом критерий использования Си таков: на нем надо писать либо то, что даст этим существенный выигрыш производительности, либо то, что по каким-то причинам не может быть написано на Java (например, OpenGL-вызовы).
Java непригодна для оперирования большими массивами данных — она слишком много времени тратит на выделение/освобождение памяти. Но если не тащить в нее многомегабайтные буфера (а в этом обычно нет необходимости), то писать на ней управляющий слой, для которого и нужно ООП — одно удовольствие.
Си же, в свою очередь, непригоден для управления. Он негибок, в нем нет должного абстрагирования и, что еще важнее, в нем нет контроля нештатных ситуаций.
Так что они превосходно дополняют друг друга.
Для разработки я применяю IDE Eclipse, в которой можно одновременно исполнять и отлаживать и C, и Java.
C++ я использую вместо Си в тех случаях, когда мне нужны математические абстракции типа векторов с переопределением операторов. Вот это Java, увы, не умеет. Зато это умеет аналогичный ей C#.
Писать контроллеры на C++ гораздо труднее. И UI тоже, даже несмотря на такие мощные инструменты, как Qt.
dougrinch
Rust?
Wedmer
Ну и D заодно. Вопрос другому человеку задавался, но я подозреваю, что оба эти языка, как и C++, этому человеку нравиться не будут.
bigfatbrowncat
D — отличный язык. Если бы я сочинял свой, он бы был очень на него похож. Но, увы, инфраструктуры в нем… нет. От слова «совсем». Если бы существовала надежная, нормально доделанная среда разработки, более-менее вменяемая стандартная библиотека, желательно, стандартизованная (или хотя бы популярная) и если бы хоть пара крупных компаний в него вложились, написав на нем что-то крупное… Искренне желаю этому языку всего вышеперечисленного.
Rust не знаю, но в лучшем случае все вышеперечисленные комментарии — про него тоже.
Wedmer
По D у вас устаревшие данные.
bigfatbrowncat
Перепроверил.
github.com/DDT-IDE/DDT/blob/latest/documentation/Features.md#features
Выглядит уже более-менее симпатично. Но по-прежнему сыро. Можно попробовать написать что-то небольшое на нем. Авось лет через 10 он появится в списке полезных технологий в собеседованиях на технические вакансии.
Wedmer
Недопроверили.
bigfatbrowncat
Так просветите. Вы же в нем разбираетесь.
Wedmer
Вы хотя бы сюда заглянули для начала.
А потом wiki.dlang.org/Current_D_Use прочли бы.
bigfatbrowncat
Да заглядывал. Какой смысл мне от того, что Facebook где-то задействовал D, если я даже не знаю, где?
Остальные названия я не знаю. И, к тому же, сам факт наличия такого списка на сайте языка означает примерно следующее: «Посмотрите, какие мы взрослые, нас даже ФЕЙСБУК заметил!»
То есть попробуйте такой список для того же пресловутого C++ составить. Или для Java.
Понимаете? Для этих языков проще перечислить, что написано НЕ на них.
Я, вместо этого, посмотрел, с чем придется столкнуться разработчику, который захочет писать на D. И вижу я только минимальную поддержку Intellisense в IDE (что, конечно, хорошо), отсутствие, например, рефакторинга кода (что означает, что авторы плагина крупные проекты не пишут), только-только более-менее отлаженную стандартную библиотеку и гордый список «Нас Заметили».
JIghtuse
Это странно. Что за версия компилятора, какие флаги сборки? Вот пример, у меня он не собирается (gcc 4.9.3):
ideone.com/M535ZN (clang 3.7)
ideone.com/42XEHl (gcc 4.3.2)
Или не было и звёздочки перед m_b? Это уже двойная забывчивость какая-то. Но и такая лечится одним запуском valgrind/cppcheck:
bigfatbrowncat
Было это пару лет назад. Звездочки перед m_b не было, потому что m_b было ссылкой. Вот только я, честно говоря, позабыл, где именно я амперсанд потерял — в конструкторе, или в декларации ссылки. Скорее, конечно, второе… Детали уже не помню. Никакого C++11 — только старый стандарт.
Я как раз этим и руководствовался — классы-модули связывал по ссылкам, а не по указателям.
Как реализовать запрет копирования, я знаю. Тогда, возможно, еще не знал, но это не важно.
Важно то, что человек, изучающий некоторую технологию, если эта технология адекватная, должен создавать работающие системы на каждом этапе своего развития.
Возьмите школьника и крутого профессионала. Дайте каждому из них Java и C++. И адекватную каждому задачу. И вы увидите, что на Java каждый напишет сообразный своему уровню программу. Код, написанный школьником на Java, будет ужасен, неэффективен и переполнен антипаттернами. Но он будет работать. Потому что все ошибки, которые создаст «юное дарование», оно же при должном усердии сможет починить. У него всё будет под контролем.
А вот на C++ он не напишет ничего. Потому что любая программа сложнее «скажите А, скажите Б, вот вам А+Б» у него будет крэшиться и требовать для исправления проблемы более глубокого уровня понимания, чем тот, которым он владеет на данном этапе. Он впоследствии, возможно, поймет, что сделал не так. Но будет поздно.
Мне трудно приводить себя в пример, так как Java я начал изучать, уже умея программировать, зная ООП и много других умных слов. Но я хорошо помню, что на Паскале (который идеологически ближе Java) мне было гораздо проще добиться рабочего кода, чем на Си.
Некоторые считают «вот не напишет школьник ничего на C++ — и хорошо нечего быдлокодерам в святыни лезть грязными руками». Но я полагаю, что это — всего лишь результат запредельной, избыточной универсальности и свободы, которую дает C++ и плохого проектирования.
0xd34df00d
Почему это?
Если вы пока ещё не научились включать дрель, то не стоит ожидать, что вы ей сможете эффективно и адекватно выполнять какую-либо работу без её включения. Гвозди забивать, шурупы вворачивать, и так далее.
bigfatbrowncat
Если бы у дрели был функциональный режим, не подразумевающий необходимости ее включать, то человек, не умеющий этого делать, должен был бы быть способен эту возможность использовать.
Я сам лет до 15-и не задумывался, для чего нужны эти странные «рога» на противоположной стороне молотка. При этом я с успехом забивал гвозди. Инструмент щедро предоставлял мне тот набор функциональности, который я был способен использовать. Вы вряд ли обрадывались бы, если бы перед использованием дрели вам приходилось сдавать экзамен не только по правилам техники безопасности (тут я ничего против не имею), но и, скажем, по ее инженерной конструкции.
Мы каждый день применяем разные системы, каждый — в меру своего понимания. Чтобы ездить на автомобиле попрежнему достаточно не путать педали и знать ПДД, а не разбираться в конструкции системы впрыска топлива (старые советские модели не рассматриваем).
(Утрирую для понимания) Представьте себе язык программирования, который требует знания высшей математики для того, чтобы написать на нем тетрис. Как считаете, хороший он? Удобный? Я сомневаюсь.
0xd34df00d
Вот и у C++ нет такого режима.
Как и у всех остальных языков, впрочем. Ну не будет падать Java-программа при выходе у этого вашего школьника, ну зато там будут объекты не удаляться-освобождаться, потому что он забыл их из коллекции удалять, когда они перестают быть нужны. Это сильно лучше, что ли?
Пишу периодически на таком языке, как Haskell. Правильно поставленные в ходе обучения различным областям математики мозги очень сильно помогают. Язык хороший. Удобный.
bigfatbrowncat
Лучше. Потому что этот школьник, написав работающую программу, одну, другую, третью, постепенно будет учиться, развиваться. И в итоге вырастет в специалиста, который создает классные продукты. Все мы когда-то «забывали объекты из коллекций удалять».
А если вы ему даете язык, на котором ему писать трудно и он не может решить ряд проблем, на котором всё глючит и ничего не работает, он делает нелестный вывод, увы, не о вас и даже не об инструменте, а об отрасли в целом.
Хорошо ли это? Я считаю, что когда люди развиваются, а не бросают всякие попытки на старте — это, несомненно, хорошо.
Что до Хаскелля, то вы слабовато взяли. Давайте прямо сразу Пролог возьмем. Вот где надо много знать, чтобы простейшую задачу решить. Только почему-то им вообще мало пользуются. И студенты, которым им мозг компостируют, вместо программирования идут компьютерные сети прокладывать. А потом — в менеджеры…
C++, в отличие от Хаскелля, позиционируется, как инструмент, пригодный для решения простых задач — UI там писать, игры… И, что характерно, именно это на нем обычно и пишут. Так уж сложилось. Но вы вряд ли найдете вменяемого человека, который станет писать графический редактор или игру на Хаскелле. Разве что из соображений обучения.
0xd34df00d
Это какой-то школьник с завышенным ЧСВ и уверенностью в собственной непогрешимости. Не надо так.
Потому что мозги компостировать надо не конкретным Прологом или Хаскелем или C++, а математикой. Компостирование конкретными языками — это уровень ПТУ, а не высшего учебного заведения.
А где можно почитать, как позиционируется Хаскель и как позиционируется C++?
Оба этих языка вроде как описываются как языки общего назначения, значит, на них обоих можно писать что игры, что UI.
А почему тех, кто таки станет, вы сразу записываете в невменяемые?
bigfatbrowncat
Нет. Просто человек слаб. И всегда обвиняет в своей слабости других. А если он еще и неопытен… Он вам ничего не скажет. Просто пойдет писать стихи.
Город Нижний Новгород. Несколько факультетов в ННГУ и НГТУ. Такие вот у нас в стране два именитых «ПТУ».
Можно. Покажите мне крупную коммерческую игру на Хаскелле и я с вами соглашусь, что он для этого удобен. Пишут, что-то больше на C# (Unity), на Java и на Python. А на C++ движки делают.
Я думаю, что функциональная парадигма в архитектурном плане менее мощна, чем ООП. Она не только сложнее для понимания, но и более хлопотна. И, да, список впечатляет.
Никого я никуда не записываю. Просто считаю, что те, кому всерьез надо игры делать, берут что-то более мейнстримовое. По куче причин.
0xd34df00d
Ну и пусть идёт. Предпочту работать с людьми посильнее.
В том числе, да. Впрочем, давайте не будем скатывать это в обсуждение образования.
Обсуждалось же уже вроде, что ядро Unity — на плюсах, на C# очень несущественный (по крайней мере, по части производительности) кусок. Вы тогда уж ещё Lua вспомните.
Где посмотреть на крупные коммерческие игры на этих языках?
Почему?
Почему это сложнее?
Кстати, вы, возможно, не поверите, но изучение хаскеля помогает писать и более хороший C++-код.
И это почему?
Wedmer
У большинства от того, что все работает, будет меньше мотивации повышать качество своего кода. Ведь работает же!
bigfatbrowncat
Мотивация повышать качество кода — резльтат любви к делу и наличия эстетического чувства. Ни то, ни другое не разовьется, если у вас ничего не получается путного с самого начала.
У меня работало. И в детстве на Паскале, и в более старшем возрасте на Delphi, затем немного на C#, потом на Java (в том числе, под Android). И иногда на C и C++ работало, но меньше и хуже. Но если C — неизбежное зло для того, кто хочет писать быстрый код под платформу, то в чем неизбежность C++, я так и не понял. Даже сообщения об ошибках при наличии в коде хоть пары шаблонов читать — кошмар сущий.
0xd34df00d
Моим первым языком был JS. Снежинки в браузере, ощущение феерической нелогичности и костыльности, и так далее. А вот потом я взял книгу по C++, и понеслась…
С сегфолтами, конечно, непонятками и так далее. И ничего, как-то вырос, не растерял любовь к делу.
Попробуйте использовать нормальные инструменты вроде clang.
Chaos_Optima
Эм… с каких пор написание UI и игр перешло в раздел простых задачь?
Wedmer
С тех же самых пор как и
bigfatbrowncat
В Си ООП нет. Это я вам могу сказать после того, как участвовал в течение 2х лет в разработке под платформу BREW (https://en.wikipedia.org/wiki/Brew), которая написана на Си и в которой ООП было на всех уровнях.
Тот кошмар с кучей шаблонов, неочевидных соглашений и жуткого путанного кода, который приходилось преодалевать — с полным отсутствием ни то чтобы контроля типов, а даже минимальной проверкой чего бы то ни было — я не считаю приемлимым.
Теоретически можно поддерживать любую парадигму на любом языке. Можно писать ООП на Си, можно писать ASM-way на Java. Просто это запредельно неудобно.
Wedmer
Вы написали чепуху. Ни в одном языке нет ООП. ООП реализуется при помощи языка. Причем он может и не быть объектно-ориентированным. А в С есть структуры, в которые можно укладывать указатели на функции. Quake 2 по сути был написан с использованием ООП но на C.
,
Mrrl
Вы под ООП понимаете только наличие виртуальных функций?
Wedmer
А вы сделайте ревью кода Q2 и сами решите. Это был всего навсего пример того, что для ООП не всегда нужен OOЯП.
bigfatbrowncat
Я написал не чепуху. Просто у нас с вами разные понятия.
Вы видите возможность использовать любую парадигму где угодно. Я ее тоже вижу. Но в отличие от вас, я рассматриваю язык как средство упрощения использования той или иной парадигмы.
Си не предназначен для ООП. В нем нет ни понятия класса, ни понятия инкапсуляции, ни полиморфизма. В рамках Си можно создать «игрушечный» полиморфизм, организованный вами же как набор указателей на функции. Можно написать
#define CLASS struct
. Можно много чего сделать. Но от этого сам язык не станет удобнее при использовании этих возможностей.Компилятор не обругает вас за попытку вызова private-метода, система контроля типов не подскажет вам, что вы присваиваете яблоки к крокодилам. Всё то, что является основными преимуществами современного ООП, совершенно не будет работать.
Вы всё будете делать «руками». За всё будете отвечать сами. А когда програма «обрушится», вы получите совершенно не читаемый стек, состоящий из функций с большим количеством подчеркиваний в именах, которые вы не вызывали (потому что они развернулись из препроцессорных макросов) и будете всякий раз гадать, в какой вообще строке у вас случился креш.
Вот именно это всё я и подразумевал под фразой «В Си ООП нет».
Wedmer
Попытка выкрутиться провалена. Вы расскажите это разработчикам Linux kernel и Asterisk.
bigfatbrowncat
Дорогой друг! Во-первых, не грубите. Я не выкручиваюсь, а дискутирую. Высказываю те аргументы, в которые верю.
Во-вторых, когда я что-то пишу, я всегда подразумеваю, что собеседник постарается понять меня, а не придумать то, что я, по его мнению, имел в виду. Буквоедства не терплю. Вы, кстати, делаете ровно так же.
А в третьих, тот факт, что во многих проектах, в которых требуются запредельные условия (например, сочетание низкого уровня и сложной архитектуры), используются инструменты, лишенные удобства для разработчика, не отрицает того, что они неудобны. Кстати, в ядро Линукса C++ не пускают. Что тоже намекает…
Еще раз: когда на использование языка обрекает нужда и выбора нет, используют то, что есть. Вот, на веб-клиенте используют JS. Ужасный язык — но что делать!?
Вы, ради бога, пишите, на чем вам удобнее. Я, к слову, вас судить не пытаюсь. И искренне вам желаю удачи в том, что вы любите — разработке крупных проектов с ООП на чистом Си и игровых движков класса ААА с браузерами на C++. И никогда не разочаровываться в этом деле.
Я же сам предпочитаю высокоуровневую архитектуру на Java и проекты, в которых не приходиться грызться с реальностью за каждый такт процессора, пренебрегая красотой кода и комфортностью инструментария. Потому что это, по моему мнению, — бестолковое занятие, которое отнимает силы и время и не содержит в себе ни капли творчества.
Wedmer
Я вам не грубил. Просто у нас с вами разные понятия.
В том то и проблема, что ваши аргументы больше смахивают на чисто ваше мнение. Хоть на статьи какие ссылки бы дали.
Слишком много разных причин, почему C++ нет в ядре Linux. От личной неприязни Линуса до тупой несовместимости. В LKML FAQ все расписано.
Хорошо оптимизированный код чаще красивее, чем что то неповоротливое.
bigfatbrowncat
Мои аргументы смахивают на мое мнение, поскольку им и являются. Я стараюсь опираться в споре на свой опыт. Гуглом могут пользоваться все.
А ваши аргументы смахивают на кучу расхожих стереотипов, подкрепленных расхожим оптимизмом. Если бы вы привели, как я,.пример из своего собственного опыта, это было бы интересно. А говорить «C++ крутой, потому что на нем кучу всего пишут» могу и я. Только вот я считаю, что основных причин его распространения 3.
1. Совместимость с Си
2. Свобода от патентных притязаний (вся платформа, стандарт и основные библиотеки полностью открыты)
3. Причина рекурсивная — моя любимая. C++ популярен оттого, что он популярен. На вопрос, на чем можно написать всё на свете, вопрошающему говорят: «Возьми кресты. Они сложные, но уж если освоишь, то удешь богом».
А еще — это соображение вас вряд ли убедит, поскольку оно уж совсем умозрительное, но я всё же скажу: чем универсальнее инструмент (а вы, надеюсь, не будете спорить, что в универсальности с C++ мало, кто может соревноваться), тем он менее удобен для каждого конкретного случая. В низком уровне мы превращаем C++ в Си, на высоком уровне мы пытаемся из него сотворить что-то наподобие C# или Java. Вот только Си проще и быстрее компилируется (просто за счет более простого синтаксиса), а в C# и Java более качественная защита от глупостей.
Где-то слышал фразу: «оптимизация — это то, чем начинают заниматься программисты, когда код слишком красив».
А если серьезно, то красив хорошо спроектированный код. На любом языке. Если хотите пример того, что я считаю красивым кодом, загляните в исходники Eclipse Platform. 15 лет истории и IBM как основной автор. Результат прекрасен. Его оптимизировали — но оптимизации носили именно архитектурный характер. И такие оптимизации лучше делать на Java — легче. А те оптимизации, о которых мы с вами говорили — это т.н. микрооптимизации — выиграть 10% производительности, чуть-чуть изменив последовательность выделения памяти и поменяв местами две командочки. Вот это я, простите, считаю суетой. Не интересно это.
Chaos_Optima
Ну вот что за чушь вы несёте.
Может вы так и делаете, но нормальные программисты используют С++ как С++. По долгу работы мне приходится использовать С#, Pyton, mel помимо С++, и на какждом из этих языков я пишу так как нужно писать на том или ином языке, никогде не писал на С++ как на С или на С#. Зато на предыдущей работе работал с людьми, которые писали на С++ как на яве, которые использовали огромные иерархии, и вместо указателей на функцию\метод использовали интерфейс IWorld, про переопределение операторов\шаблоны вообще неслышали, а для того чтобы добавить класс в фабрику приходилось править 5 файлов вместо того чтобы один раз написать макрос и шаблон, и использовать каждый раз в одном месте.
Вот когда пишешь на одном языке как на другом, вот тогда и появляются проблемы, возможно вы пытались в своей практике использовать С++ как С# или Java, отсюда у вас и все проблемы с этим языком.
bigfatbrowncat
Я очень жалею, что в Java нет переопределения операторов. Она бы стала лучше от этого.
Я очень жалею, что ни в Java, ни в C++ нет свойств. Без них код более унылый. В C++ я свойства эмулировал с помощью шаблонов и макроопределений, но это было не то…
Я очень жалею, что ни в C++, ни в C# нет внутренних нестатических классов. Более того, большинство людей, которые не исали на Java, даже не понимают, что это такое и для чего они могут быть полезны…
Я могу много фич перечислить, которые прекрасно работают в одном из этих языков, но отсутствуют в другом. В каждом из них я стараюсь использовать то, что он предоставляет.
Но это всё — мелочи.
С точки зрения проектирования Listener в Java и event в C# — совершенно одно и то же (да, я знаю, что event-у можно назначить сразу несколько обработчиков, Listener тоже можно сделать таким, просто это — менее удобно). И многословность Java тоже не особенно приятна, но она компенсируется мощнейшими средствами IDE.
Все три языка на самом деле очень сильно похожи. Поэтому их можно и нужно сравнивать. И сравнение это будет явно не в пользу C++, когда дело касается надежности кода. Единственное, чем C++ может похвастаться — это отсутствием дополнительного runtime-а для исполнения кода.
И еще.
Я сначала изучил C++ (по крайней мере на уровне книги Герберта Шилдта), а только потом появился язык C#. А про Java я вообще узнал только 5 лет назад.
Почему вы считаете, что если для меня эти языки похожи, я не понимаю между ними разницы? Давайте просто допустим, что эта разница несущественна.
Chaos_Optima
Вообще не понимаю к чему вы привели эти примеры, я говорил не про фичи конкретных языков а про процесс проектирования приложения. (и да внутринние классы в С++ это friend классы, ну и да всё что вы привели фигня, единственное чего не хватает С++ это рефлекшена)
Ну вообщето далеко не одно и тоже, Listener в яве требует наследования от интерфейса. Отсюда в яве рождается куча ненужных однострочных интерфейсов, либо же интерфейсов при реализации которых приходится писать пустые функции, в С# же и в С++ этого можно избежать. Программисты на проекте были java way, а потому использовали интерфейсы, а т.к. городить кучу интерфейсов им было лен, они решили использовать С стиль и передавать идентификатор и указатель на войд. Вот что в нашем случае означало писать на С++ как на яве и как на С, и именно из-за вот таких вот «решений» вырисовываются поистине эпические ошибки, патерны это конечно хорошо, но их тоже нужно уметь и писать и применять, в зависимости от языка.
Я вспоминаю как в универе на яве писал что-то, и для того чтобы обработать событе от кнопки, IDE автоматически реализовывала полностью весь интерфейс, тобишь генерировала дополнительно ещё пару десятков пустых функций, вы про эти мощьнейшие средства IDE? И причём тут многословность?
Все эти три языка на самом деле сильно разные, пичально что вы это не видите. Каждый из этих языков затачивается под свои задачи, да они пересекаются но не так часто как вам кажется.
А ещё скоростью, а ещё мультиплатформенностью, а ещё гибкостью в плане управления памятью, гибкостью в оптимизации, и тд и тп.
Вы не понимаете между ними разницы, потому что вы считаете что эта разница несущественна.
А я начинал с Delphi а после 5 лет когда поступил в универ познакомился с С++ (на уровне книг Страуструпа, Майерса, Александреску) и прочими языками, а после 5 лет обучения на этих языках в универе, я 6 лет работаю с С++, и попрежнему продалжаю учится программированию. Только вот я не понимаю как эта информация поможет вам? Также как не понимаю зачем вы это написали мне.
bigfatbrowncat
Окей. Давайте тогда я тоже вспомню, что в школе программировал на Delphi. А когда увидел C++, он мне показался очень запутанным и неудобным. Учить я его начал в первую очередь именно потому, что не видел альтернативы. Когда появился C#, я эту альтернативу увидел. Но я еще достаточно долго пытался иногда писать программы на C++ целиком. Всякий раз получалось хлопотно и времени уходило больше, чем на C#.
Серьезно? Вот уж я не знал! Последние 3 года пишу сперва под Android, сейчас портирую яву на новую платформу, был даже контрибьютором в стандартную библиотеку альтернативной Java-машины Avian и — ужас — не знал, что Listener — это анонимный локальный класс.
Вы понимаете, что с точки зрения архитектуры приложения не имеет значения, создается в данном месте анонимный класс, указатель на функцию, определенный через typedef или делегат? Ну не важно это. Главное — что тут имеет место механизм callback-а.
Конечно они разные и я это вижу! Но если вы мне дадите программу на C#, я переведу ее вам на Java, воспользовавшись терминами Java и использовав ее преимущества, но программа при этом почти не изменится. То же самое можно сказать про C++. Только там будет сложнее с низкоуровневым дизайном, поэтому кусок кода, возможно, придется загнать под JNI (и эффективнее).
Я еще раз вам повторю. Я умею использовать все эти три языка (возможно, C# знаю не так хорошо, так как на работе им не занимался, но зато много писал для себя). Просто вы вместо того чтобы посмотреть на картину в целом и оценить общую концепцию, лезете в крайности.
Был C++. Мощный и универсальный, но местами неудобный, опасный и кривой. Люди, которых он раздражал, сделали Java, чтобы не спотыкаться о проблемы с памятью и избыточную сложность. Получилось лучше, но они выпилили кучу полезных вещей. Поэтому люди, которых Java задолбала «необходимостью имплеменировать целый интерфейс», которую я и назвал излишней многословностью, а также некоторыми другими своими недостатками, создали C#.
Большая часть ваших аргументов (про скорость и управление памятью) верна. Это — те причины, по которым я часто использую JNI. Но вот это:
насмешило. Честно. Я лет пять искал способ научиться написать проект на g++ так, чтобы потом нормально собрать его под Windows. Или вы мне сейчас начнете про ANSI рассказывать? Давайте я тогда вам в ответ расскажу про прекрасный модификатор «b» в fopen, который, если забыть его на Windows, приведет к тому, что из вашего прочитанного бинаря волшебным образом исчезнут все символы с кодом 13. Я как-то долго отлаживал кусок libcore, который портировал с Linux на Windows и не мог понять, почему у меня картинка не читается с диска. Повеселился на славу.
Единственный по-настоящему кроссплатформенный язык в этой тройке — это Java. И то только если вы используете «родную» библиотеку классов от Oracle. Возьмите стек технологий, на которых основан eclipse — будет вам мультиплатформенность.
Только не надо мне говорить про Qt. Как только он станет частью C++ или хотя бы обзаведется лицензией, позволяющей его использовать в проектах любой степени коммерциализованности без всяких приседаний перед авторами и выплат, я рассмотрю его как приемлимую альтернативу. Кстати, в нем есть кроссплатформенные файловые потоки?
Chaos_Optima
Вы хотя бы до конца комментарий прочитали? Зачем вы пишите эту инфу???
И что? От этого не изменится тот факт, что этот класс должен реализовывать интерфейс.
В том то вся и фишка, что важно, каждый элемент должен выполнять свою функцию.
Практически любой код, на любом языке, можно переписать на другом, но это не значит что языки похожи, каждый язык используется там где он нужен, я могу дать вам кусок рендера на С++, но когда вы его перепишите то получится что 80% кода будет написано на JNI, и какой тогда смысл брать яву?
Не видно, судя по той чуше что вы пишите, вы знаете только яву.
мда, это говорит лишь о ваших способностях, а не о языке.
В очередной раз хотите похвастаться своей безграмотностью в С++? Я не против. ))
Не видел ни одного примера, на яве для IOS, xbox, ps. И где ваша хвалёная мультиплатформенность?
Если бы я говорил про какую-то универсальную библиотеку то я скорее взял бы буст.
Есть, а чем вам не угодили потоки из stl?
0xd34df00d
Они всё-таки почти ортогональны. На бусте не сделаешь графический интерфейс и не постучишься в БД, на Qt не сделаешь топологическую сортировку, не посчитаешь диаграммы Вороного и не упорешься темплейтами.
Мне, мне не угодили! Я так и не осилил их ускорить и избавиться от ненужных в моём случае операций с локалью и чем-то там ещё внутри
istream::operator>>()
. Пришлось рукамиmmap()
ить.bigfatbrowncat
Нет. Просто мне попал в руки кусок кода на C/C++, который был написан для Linux и который надо было портировать на Windows. Увы, код был написан безграмотными разработчиками из компании… как бишь ее… Google, кажется, которые не знали про потоки в stl.
Лично мне потоки из stl не нравятся двумя вещами: во-первых, они неудобны, во-вторых — медленны. Возможно, так же считали и люди, делавшие backend в libcore.
А вы знаете, какие титанические усилия Apple прикладывает к тому, чтобы не пустить Java на iOS? Они этой конкуренции боятся как чумы. Потому что весь стек технологий (XCode etc.) в подметки не годится оному из Java.
0xd34df00d
Если честно, ваши комментарии здесь вызывают сомнения о применимости этого утверждения к C++.
Некроссплатформенный код можно писать и на нём. Лично видел клиент-банк, который дёргает виндовые DLL, отчего код не запускается больше вообще нигде, и одной лишней буковкой
b
тут не отделаешься.Зачем?
А у Оракла у Гуглу иск типа на пустом месте?
bigfatbrowncat
Иск Оракла Гугл вполне успешно отбил по всем пунктам, кроме сущей ерунды. Никто не может «приватизировать» API. Можно считать проприетарной только реализацию.
То, что можно некроссплатформенный код писать на чем угодно, я согласен. Но не нужно подменять понятия. Я вам сказал, что Java кроссплатформенна, поскольку на ней надо очень постараться, чтобы написать что-то платформо-специфичное. А если ограничиться чисто Java-инфраструктурой (а там есть очень-очень много кода и от apache foundation и от куч других), то вообще почти невозможно. Конечно, это не отменяет дураков, которые в Java-коде прибавляют бекслеши к путям. Тут уж ничто не поможет.
Зачем надо, чтобы вся инфраструктура была стандартизована и являлась частью набора инструментов разработчика? Ответ очень простой — чтобы не было зоопарка. Один станарт — один подход — меньше всего изучать. К тому же, чем меньше кода, тем выше его качество. Этим сильна Java. На ней редко кто-то пишет то же самое по второму разу. Просто такая культура.
0xd34df00d
А у вас, вот лично у вас, хватит ресурсов на адвокатов, если что, дабы отбиться?
Бекслеши в путях в жаве — это они дураки, значит, по-вашему. А fopen и b в плюсокоде — ни в коем случае не дурость. Или не дурость — такая дырявая абстракция, которая, к сожалению, есть в ОС.
Да, зачем надо? Зачем надо стандартизовать GUI, работу с графами, математику и кучу всего ещё? Это замедляет прогресс и не даёт права на ошибки, потому что обратная совместимость, которую потом придётся тянуть.
Как будто это что-то плохое.
А вот этого я не понимаю, серьёзно. Это же так прекрасно, изучать что-то новое! В этом весь смысл и весь кайф!
Да даже в мелочах, вот написал я сейчас парсер один с parsec'ом, получил то-то. Теперь в силу ряда причин охота на той же задаче attoparsec попробовать — а какие у него будут сообщения об ошибках при разборе? А получится ли легко и просто перенести код с одного на другой? А какой прирост в производительности и памяти я получу?
Не, серьёзно, если страшно и неохота что-то изучать, то не надо идти в программисты.
Неочевидно, мягко скажем. И звучит для меня, если честно, примерно как «меньше производителей на рынке — выше качество продукта».
bigfatbrowncat
Вот тут вы меня, думаю, поймете, когда перед вами встанет задача написать программу, большую и хорошую, на основе пары-тройки имеющихся open-source — решений и вам, вместо того, чтобы изучать их абстракции, придется разбираться, как их вместе слинковать, чтобы собралось и не падало.
Я люблю изучать то, что интересно, а не всё подряд. И люблю стабильность. То есть если не хочется писать всё from scratch, то берешь качественно сделанную платформу и не лезешь в нее. По крайней мере, на первом этапе разработки. Понимаете?
А еще больше я люблю создавать, а не изучать. Как говорится, чукча не читатель…
Странное сравнение. Когда кода мало, а вылизывают его сотни разработчиков, он становится безупречен. Пойдите, попробуйте найти ошибку в ecj, например. А ведь это — компилятор — довольно непростая штука.
А если бы компиляторов Java было не 2, а 20 (как у Питона, например), то усилия всех этих людей были бы распылены на них и каждый в отдельности был бы хуже.
В open-source, где в любой момент код можно беспрепятственно форкнуть, конкуренция — зло. Такая вот «коммунистическая» идеология.
0xd34df00d
Почему-то такого опыта у меня нет. Не поделитесь своим? Просто ради интереса.
Стабильность как отсутствие изменений — скучно. Стабильность как отсутствие падений — не противоречит зоопарку.
Я не представляю, как так можно жить, кроме совсем базовых вещей вроде стандартной библиотеки языка (да и там, на самом деле, лезть надо, чтобы понимать, чем
map
отunordered_map
отличается, просто внутренности привычны почти с детства).А вот нужно мне проверить возможность использовать key-value-хранилище на диске, например, и среди прочих кандидатов сразу вылезают LevelDB и RocksDB, которая является продолжением LevelDB. Как можно не почитать, чем они отличаются, что там сделано, какой принцип работы?
Таки перед тем, как создать что-то адекватное и полезное, стоит узнать, что уже есть.
Нет. Вы почему-то считаете, что если бы не было какого-нибудь там PyPy или какой-нибудь экзотики, то люди, пилящие эту экзотику, срочно кинулись бы писать основной компилятор. Это очевидно не так.
Вторя моему предыдущему пункту, в опенсорсе кроме альтернатив «пилить проект» и «форкнуть проект» есть вариант «не пилить ничего». И есть немаленький шанс, что люди, если вы им запретите второй вариант, выберут третий, а не первый.
Если бы мне внезапно запретили писать мой IM-клиент, я бы не пошёл дописывать Psi или Pidgin, я бы пошёл заниматься матаном.
bigfatbrowncat
Да на здоровье. Взял я как-то Android Libcore. И решил собрать его под Desktop. В нем кучка разных кусков — там и makefile, и autotools и еще кое-что. И всё это надо настроить, пропатчить…
Ладно, черт с ним. Дорботали напильником, собрали, потом месяц учили его работать под Win32 (читайте выше историю про «b» в fopen), а потом надо было эту всю хрень сцепить с еще одним куском кода, который собирался уже с помощью cmake.
Хорошо, что там хоть scons нигде не было… Понимаете? Если бы система сборки была одна, то я бы в худшем случае ее одну знал и настраивал, а не скреплял кривыми костылями кучу makefile-ов.
Дело в том, что желание «пилить экзотику» обычно возникает либо у студентов-экспериментаторов, либо у людей, недовольных мейнстримом по каким-либо объективным причинам. По лицензионным. По политическим. Или просто по причине неисправимой косячности этого мейнстрима.
Предлагаю простой способ оценки доработанности той или иной технологии. Достаточно узнать, сколько у этой технологии альтернативных реализаций. Чем их меньше, тем технология лучше. Вот, например, вы много видели автомобилей с педалями под рулем или за пяткой? Ни одного. Или, скажем, ружье со спусковым крючком под подбородок…
А сколько в мире дистрибутивов Linux под десктоп? Их сосчитать не могут — даже те, у которыхбольше одного мейнтейнера. И, кстати, по мере «взросления» дистрибутива Ubuntu, он перетянул на себя внимание большей части Linux-пользователей и разработчиков, поскольку вложить силы во что-то популярное, согаситесь, приятнее.
Вот у Java был официальный компилятор. IBM он не устроил своей лицензией (и, видимо, имевшимися в тот момент хилыми возможностями), и они написали ECJ. Теперь компилятора два. Между ними есть различия и опытные разработчики их знают. ECJ строже к стандартам, но при этом он умеет делать классы из синтаксически некорректных файлов. А еще он быстрее.
И хотя Java по популярности обгоняет почти все остальные языки, для нее до сих пор ровно 2 компилятора. И я нигде не слышал, чтобы кто-то пытался сваять третий. Зачем? IDE — как минимум три штуки. А компилера — 2.
А теперь подсчитайте, сколько на свете компиляторов C/C++ под одну только платформу x86. Не учитывайте устаревшие и заброшенные — берите только актуальные. Пальцев не хватит сосчитать.
Кстати, JVM достаточно много, но как правило они пишутся по одной из двух причин: оптимизированность под конкретное железо и лицензионные соображения. И обычно тоже максимум 2-3 штуки на платформу. Потому что там всё (относительно) просто и ясно. Что еще улучшать?
Я не утверждаю, что Java — свет в окошке, а C++ надо выкинуть. Просто степень доработанности Java как инфраструктуры значительно выше, хотя она заметно моложе, чем C++. Ее лучше спроектировали.
Нет этого варианта. Дорабатываются open-source проекты в основном по одной и той же практической причине: вам нужна функциональность (исправленная бага), а автор ушел в запой на месяц. А если вы допиливаете столько, что вам начинает казаться, что работаете вы больше него, то вы делаете форк. И никуда вы не денитесь от этого. Можете только закрыть свой проект или оставить открытым (если не GPL). Вот и весь ваш выбор.
0xd34df00d
Весело, конечно. В очередной раз порадовался, что я разрабатываю под линуксом, установку всяких опенсорс-решений за меня делает пакетный менеджер, и так далее.
Вы так говорите, будто это что-то плохое.
Более того, никто не мешает быть студентом-экспериментатором в послерабочее время, даже если вы уже закончили вуз и работаете фуллтайм.
Неочевидно, что он адекватен.
Не соглашусь. Вышеприведённый мой опенсорс-проект недоступен под убунтой и, вероятно, в силу разных причин никогда не будет доступен. Нет мейнтейнера (и мне лень и неинтересно его искать), я зачастую использую слишком новый тулчейн (gcc сборку не осиливает, только clang, а как выйдет C++17, так снова ещё проблем добавится), и так далее.
Ну, как минимум, IcedTea ещё. Да и, помнится мне, в этой моей генточке аж три разных пакета для
dev-java/*-jdk
. Далеко от системы сейчас, не посмотрю.gcj ещё есть, из сходу приходящего в голову. Не знаю правда, компилятор это ли, виртуальная машина ли, или нечто между.
Хватило, даже одной руки: clang, gcc, icc, ну и cl.exe ещё, вероятно.
Думается мне, фортран гарантированно проще, старше и так далее, чем C++, но почему-то компиляторов у него тоже немало.
А вот для хаскеля, считайте, один ghc есть из мейнстримных, если я правильно понимаю экосистему. И это очень печально, хоть, конечно, компилятор и очень мощный.
Если вы свободный разработчик, делающий свой проект, то по-прежнему есть. Хрен с ней, с багой чужой, пойду сам бухну.
Wedmer
С++ еще у (Open) Watcom есть. Но там есть проблемы с развитием. А когда то был лучший набор для C/F77.
0xd34df00d
Ну, там в условии была актуальность и незаброшенность компилятора. Лично для меня актуальность — поддержка C++14 во второй половине 2015 года. Что-то мне подсказывает, что Open Watcom даже про C++11 не очень слышал.
bigfatbrowncat
Вот тут вы меня заставили улыбнуться. Я уже несколько лет не разрабатывал что-либо не-кроссплатформенное. И выработал-таки себе toolchain, позволяющий писать на C/C++ кроссплатформенные приложения с UI на JVM, при этом не используя не только проприетарные решения в готовом продукте, но и GPL. Только MIT, BSD и Apache.
Какой смысл писать программу, которая зависит от конкретной платформы или от прихоти держателей Виндоуса или Мака. Так ведь :)
Я не считаю, что быть студентом-экспериментатором — плохо. Просто я не рассматриваю всерьез их вклад в развитие индустрии. (Я допускаю, что есть вундеркинды, но в среднем они должны сперва научиться, чтобы начать делать что-то полезное).
IcedTea не содержит в себе компилятор. Вот выдержка с оф. сайта:
Вот это — «any 1.5 compliant Java compiler» намекает, что компилятор надо взять где-то снаружи. Хотя я сам не работал с IcedTea, гарантии дать не могу.
А вот ECJ я разбирал вплоть до исходников. И он как раз и является единственным известным мне компилятором Java, кроме javac от Oracle. Хотя википедия его не приводит, а приводит ныне покойный GCJ, который вы, видимо, считаете полноценным компилятором, которым он не является в силу ограничений интроспекции, несовместимых с мало-мальски продвинутым JVM-кодом. Потому, кстати, и заброшен. ECJ, кстати, — структурная часть Eclipse. Я выше уже писал…
Куча их: вот. Хотя их надо все палочкой потыкать на предмет свежести.
Я сейчас обидную вещь скажу. Хотите популярности вашего продукта — будьте на виду. И сделайте то, чем легко пользоваться везде. Даже специалисты, когда есть выбор, предпочтут из двух конкурирующих технологий (при равных возможностях) ту, которая в их ОС устанавливается одним «apt-get install». Такой уж закон в наших с вами «джунглях». Хотя, он нарушается, если вы ваяете что-то настолько уникальное в смысле предметной области, что ради этого не влом ставить какую-то экзотическую ОСь.
Посмотрите вот сюда. Вас же не удивит, например, что для Go и CoffeeScript только один компилятор? Просто уровень популярности Хаскелля таков, что при том уровне требований, которые к нему предъявляются, в основном компиляторе не было найдено ни одного принципиального изъяна.
В мейнстриме одним единственным компилятором могут похвастаться только Ruby (очень классный язык, учитывая, каких на нем монстров люди ваяют в одиночку) да PHP, «неповторимая глюкавость» которого не позволяет создать альтернативную имплементацию.
И, кстати, я не считаю, что хаскелль плох. Я не могу так считать, поскольку почти не знаком с ним. Но я знаю, что он — не для мейнстрима. Он, по сути, — язык «для гиков». Разве нет?
А вот здесь я даже не знаю, что вам сказать… Вы, наверное, не видели фортран. А я знаю по меньшей мере трех людей, кто пишет на нем профессионально несколько лет. Двое из них — это моя жена и мой отец (жена — физик-теоретик, отец — инженер). И могу сказать, что более ужасно нагороженного (слово «спроектированный» здесь никак не подойдет) языка даже представить себе нельзя. В качестве примера фортрановского безумия люблю приводить то, что все переменные во функции по умолчанию передаются по указателю. Это делает отладку больших проектов просто феерической, особенно, если учесть, что многие Fortran-разработчики пользуются этой «милой особенностью» как полезной функцией.
Вариант бросить любимое дело и пойти играть в нарды я не рассматриваю здесь. Это был бы явный оффтоп.
Вам мой ритерий может не нравиться. Он чисто эмпирический и ничем, кроме своего опыта, я его доказать не могу.
Wedmer
Если внимательно посмотреть, один и тот же компилятор там присутствует несколько раз.
Я видел. Очень хороший язык для математических задач.
bigfatbrowncat
C# — очень хороший язык для математических задач. Быстро считает, безопасный, легко создать UI при желании и необходимости, есть переопределение операторов и хорошие исключения. Maple — хороший язык для математических задач. Умеет доказывать теоремы и решать уравнения аналитически. MatLab — неплохой язык для работы с большими массивами данных. C++ — приемлимый язык для математических задач, если «умеешь его готовить». Просто потому что исключительно быстрый и позволяет абстрагироваться.
Сам я использовал (в большей или меньшей степени) всё это, когда учился, и немного после этого.
Среди моих ближайших знакомых есть примерно 10 человек, которые пользуются по крайней мере двумя из перечисленных инструментов (каждый — своими). И среди тех, кто владеет любыми двумя из перечисленных, и еще Фортраном, абсолютно все считают Фортран в лучшем случае неизбежным злом, в худшем — кадавром, которого следует закопать. Из преимуществ — только огромное наследие. Люди написали много матобеспечения на нем и теперь вынуждены развивать то, что работает.
Я не верю, что вы найдете хоть одного человека, которому было бы менее сорока лет, который бы умел программировать, который бы знал хотя бы пару из приведенных технологий, и который бы при этом защищал Фортран. Во всяком случае, на факультете ВШОПФ в Нижнем Новгороде, где я учился, таких не было.
Фортран является нелогичным, плохоструктурированным, небезопасным нагромождением анахронизмов.
Если вы этого не понимаете искренне, я вам это втолковать не смогу.
Если вы спорите просто так, ради того, чтобы подействовать оппоненту на нервы (а ваша манера упорно отрицать абсолютно всё, что я говорю, и придираться к каждому слову, которое можно истолковать двояко, всё больше на это указывает), то лучше займитесь чем-нибудь более продуктивным.
В любом случае я эту дискуссию продолжать смысла не вижу.
Wedmer
Действительно, смысла нет. У вас мания преследования, и я не желаю ее развивать.
0serg
Идеальный языки для прототипирования математических задач это, имхо, Python :). Шарп и плюсы проигрывают ему со свистом в обилии легкодоступных инструментов и удобстве их использования.
0xd34df00d
Не могу с вами согласиться. Вероятно, потому, что патологически не переношу динамически типизированные языки для чего-либо, кроме одноразовых скриптов на 20 строк.
Лучше уж плюсы и хаскель, для моих задач даже прототипировать вполне удобно, с инструментами проблем нет. Ну или R ещё, тоже получше питона, специализированная такая штука.
0serg
Ну, я тоже не переношу динамически типизированные языки и был сильно против питона пока не попробовал :). Прямо в консоли половину дел можно сделать. А раньше думал — зачем питону этот режим консольного интерпретатора? Оказывается в массе случаев — крайне удобно. Есть такая софтика Spyder — по сути сборка питона с простенькой IDE и расширенным набором библиотек из коробки, и там очень неплохо эта тема обыграна. На большие проекты естественно отмасштабировать будет трудно, но до 10k строчек работает вполне комфортно и для прототипирования — самое оно.
bigfatbrowncat
Если вы можете удержать всю логику программы в голове одновременно, то всё равно, на чем ее писать и как. Где синтаксис привлекательнее, на том и пишите.
А если задача серьезная, то создавать ее надо начать с того, чтобы посидеть дня три (или неделю) над моделью и над архитектурой. А когда вы ее придумаете, вряд ли вы среди питона и C# выберете питон (при равном уровне квалификации).
0serg
Я же написал — «для прототипирования».
Нормальные задачи естественно требуют подумать над моделью и архитектурой.
Шарп тут кстати между сциллой и харибдой оказывается — для прототипирования удобнее питон, для нормальной реализации — плюсы. А шарп — ни рыба и ни мясо.
bigfatbrowncat
Про легкодостпные инструменты, я бы поспорил. Я сравнивал PyCharm с Eclipse CDT и CDT была намного мощнее. Хотя, конечно, Python надо сравнивать с C# и Java, а не с C++. Разная весовая категория…
0serg
Мощность и простота использования — разные вещи. Мощная IDE с богатыми возможностями для рефакторинга, юнит тестирования и проектов на десятки тысяч файлов для прототипа мне нужна как собаке пятая нога. А вот удобный и простой рантайм где все правится на лету, богатейшие по функционалу и подключаемые в одну команду библиотеки, плюс приличное количество примеров использования в Сети оказываются очень к месту.
0xd34df00d
Я пишу под более-менее POSIX-совместимые платформы, где есть gcc или clang. Лучше clang, да. Линуксы, FreeBSD, даже OS X.
Просто как-то так складывается моя профессиональная жизнь, что пишу я в основном серверное ПО, и оно почему-то в основном крутится на линуксе.
Хотя формально моя опенсорс-поделка кроссплатформенна (с точностью до поддержки платформ Qt и boost'ом), например, просто есть проблемы с достаточно совместимыми компиляторами :)
Да и подавляющая часть рабочего кода тоже кроссплатформенна, с точностью до пары системоспецифичных вызовов, призванных тонко настроить пару узких мест.
А зря. Не даром же ходит шутка, мол, что как только PhD защищён, ресёрч-проект закрывается.
Видимо, впрочем, у нас слегка разные представления о том, что является вкладом в индустрию.
Я тоже. В то далёкое время, когда мне оно ещё зачем-то было нужно, я просто заметил, что icedtea-jdk за собой больше ничего не тянет, так что, видимо, самодостаточен. Могу ошибаться, конечно.
Ну, тут уж я ничего не знаю, ибо не работал с ним. Однако, как видите, он всё равно есть, зачем-то написан, тянется с новыми релизами gcc, и так далее. И я, был бы неофитом, запутался бы, и пришлось разбираться, что там да как. Так что всё не так уж просто, как вы сначала писали про два компилятора :)
Это не куча, это, простите, хрень какая-то. Приводить в этом списке Dev-Cpp, который по сути IDE для Windows поверх gcc, это несерьёзно. Дальше даже смотреть неохота.
А я сейчас скажу неочевидную вещь: я не хочу популярности. Толку-то с неё? Репа на гитхабе есть, работодателю потенциальному показать можно (хотя и всё больше несерьёзно, моя область всё больше сдвигается от «C++-разработчик» к чему-то с машинным обучением), что ещё надо? А так была бы какая-то там ответственность, внутренняя, моральная, ощущение долженствования неочерченному кругу пользователей, а мне этого и так хватает. Проще сразу сказать: в гробу я видал всю эту популярность, лучше буду делать действительно для себя, кому надо — тот поставит.
Gentoo не такая уж и экзотическая. И она не ради проекта, просто так получилось, что мне под ней вообще удобнее.
Да не в изъянах даже дело, а в экспериментах. Альтернативные компиляторы нужны не только для того, чтобы, собственно, предоставить альтернативную реализацию языка, а ещё и для того, чтобы поиграться с некоторыми экспериментальными возможностями. Не знаю, расширения системы типов, автоматическое распараллеливание на GPU, какие-то другие ништяки для параллелизма, да мало ли. А это всё, если и делается, зачастую пихается в бедный несчастный ghc, который не резиновый всё-таки. Мне, с одной стороны, это приятно, потому что тем фичастей компилятор доступен, с другой — как-то интуитивно это не нравится.
Плотно не работал, вы правы. Мне достаточно того, что он весьма широко используется.
А хреновый код от математиков, физиков-теоретиков и так далее, я видел и на матлабе, и на плюсах, и на чём угодно. Ну не надо потому что ожидать, что не-программисты будут писать хороший на наш с вами взгляд код вот так сходу.
Но это, тем не менее, вполне себе вариант, и не рассматривать его было бы ошибочно.
bigfatbrowncat
Пардон, не дочитал:
А задайтесь вопросом, откуда у вас вообще возникло желание написать свой IM «с блекджеком...» У меня, кстати, такое желание тоже периодически возникает! И я знаю, почему. Точно знаю.
Причина в том, что имеющиеся меня не устраивают. Они по большинству своему ненадежны, некроссплатформенны, коррумпированны или платны. Вот и притягивает эта задача девелоперов как огонь мотылька. Чем большее число пользователей не устраивает имеющаяся ситуация — тем больше «пильщиков».
0xd34df00d
А у меня не так. Мне просто нравится писать код.
bigfatbrowncat
Мне тоже нравится. Вам всё равно, какой код писать? Мне — нет. Я пишу тот код, который мне интересно писать. И полагал, что вы поступаете аналогично.
Я же не изучал «фокус-группу». И маркетинговые исследования не проводил. Просто я осознаю (предполагаю), по какой причине мне хочется того или иного. Стараюсь понимать причину своих устремлений. Это полезно.
0xd34df00d
Не всегда я даже его для себя пишу. Попросили меня добавить поддержку Tox — я этим и занимаюсь, хотя мне самому лично Tox не особо нужен. Ну, как пример.
withkittens
Chaos_Optima,
Извините, что вклиниваюсь в ваш жаркий спор, но таки мне самому интересно.Ок.
Ок.
И таки в чём принципиальная разница? И там, и сям паттерн Listener. В Java он многословный, а чтобы подписать несколько колбэков, нужно сгородить собственный адаптер (каюсь, могу не знать, есть ли в стандартной библиотеке что-то уже готовое). В C# паттерн встроен в язык, оттого он и проще.
Оппонент вам пишет, что в одном и другом случае, это механизм колбэков — вызов чего-то там в ответ на произошедшее событие. Реализация — разная, смысл — один.
По-моему, вы стараетесь к чему-нибудь прицепиться и придраться, попутно принижая оппонента — «вы не знаете то, вы не знаете сё». Большой Толстый Коричневый Кот выражает своё мнение, вы можете быть с ним не согласны, но вы переходите на личности, а Кот — нет. Так дискуссия продолжаться не должна.
Я не его адвокат, но я так вижу как сторонний наблюдатель.
Chaos_Optima
Принципиально разницы нет. Разница в проектировании и реализации, если реализовывать этот патерн в С++ как на яве то код усложнится, отсюда и последствия (на своём опыте столкнулся с этой проблемой). Критика была не в сторону того что лиснер в проекте на яве нужно использовать, а в проекте на С++ не нужно. Критика изначально была в том, что на С++ не нужно писать как на яве.
bigfatbrowncat
Покажите мне, где я вам сказал, что в C++ НАДО писать как в Java. Я такого не говорил ни разу.
Я говорил, что C++ на высоком уровне принимает парадигмы и вид, очень сильно напоминающий Java, только без кучи ее плюшек и защиты. Понимаете? Универсальность влечет за собой понижение комфорта исвользования в каждом конкретном случае. Молоток с одной стороны, отвертка с другой, перочинный нож вместо рукоятки. Удобно пользоваться?
Вы просто слишком буквально меня поняли.
bigfatbrowncat
Спасибо вам на добром слове. Я думаю, что просто товарища раздражает моя манера смотреть на языки и парадигмы со стороны архитектуры, а не языковых конструкций. Ему это кажется высокомерным.
Кстати, недавно увидел классный язык, почти всем мне нравящийся (кроме мелочей). Он на JVM, то есть Java-совместим в нем есть свойства, переопределяемые операторы, константная корректность… Жаль, что там отказались от checked exceptions (это — одна из мощнейших фич Java, по-моему). Kotlin называется. Видели?
withkittens
Если вопрос не мне, то считайте что это ответ в пустоту :)
Слышал про Котлин и про Скалу. Но не имел дела с ними.
Взять тот же Андроид — надеюсь, на них можно под него писать, поскольку голая Java (а на андроид она ещё и без лямбд!) лично у меня вызывает мгновенное оторжение из-за многословности и топорности. Listener — хороший тому пример.
P.S. Си-шарпщик.
bigfatbrowncat
Котлин — наверняка поддерживается. Так как сейчас Гуглы взяли в основу Android Studio платформу IntelliJ, а именно эти ребята его придумали, думаю, проблем нет. Там же только компиляция, по сути, отличается. Хотя API рассчитана на Java…
Wedmer
Из своего опыта я приводил уже пример где то ближе к стволу дерева.
Я просто приводил общеизвестные факты.
Как раз популярность C++ и кроется в его универсальности.
C++ для низкого уровня не надо превращать в C. Более того, местами C++ не совместим с C. Что касается верхнего уровня, то разница только в том, что можно использовать чуть более тяжелые вещи, но не обязательно. И не надо городить дополнительный слой взаимодействия.
Здесь не могу не согласиться. Когда все хорошо спроектировано, то и оптимизаций меньше надо, если они вообще нужны будут.
bigfatbrowncat
Ну слава макаронному монстру, хоть в чем-то договорились!
C++ несовместим с C в смысле своих возможностей. И необходимости писать extern «C», чтобы избежать name mangling-а. И еще пара мелочей… Ну и что? Всё равно когда вам нужен очень-очень эффективный код, вы перестанете использовать vector и возьметесь за старый добрый int[]. Возможно, вы вместо malloc, напишете new. Не исключаю, что в суперэффективном коде у вас пару раз проскочит какой-то класс. Но в основном это будет код на Си. Потому что высокоуровневые абстракции едят производительность. Вы не можете даже полиморфизм толком использовать, потому что vtable — это лишний резолв указателя при каждом вызове функции. Всё, что останется от C++ — это статические касты… и C. Вот такой нижний уровень.
А на верхнем уровне вы упретесь в необходимость либо линковать код в один бинарь, либо бить его на кучу динамических библиотек. Компиляция долгая… ОЧЕНЬ долгая (привет Джаве, где строятся отдельные class-файлы в проекте любого масштаба). Ошибки связывания, необходимость указывать библиотеки глупому линковщику в правильном порядке. Сборочная система CMake хороша, но ее придется учить. Старый-добрый make требует написать целую программу, которая соберет вашу программу. А про autotools я и говорить не хочу…
Я честно не знаю, о чем думал человек, придумавший разделение на .h и .c/.cpp файлы в виде простого включения одних в другие. Даже в древнем Turbo Pascal-е линковщик был умнее.
Эти недостатки общие для С и C++, но на C все-таки редко пишут проекты, содержащие 1000 файлов. (Пишут конечно, но это — вообще ад кромешный). А С++ на это всерьез претендует — так почему бы хоть сборочную инфраструктуру не включить в стандарт языка, как это сделано в Java и C#?
О такой мелочи как аналог JavaDoc я и говорить боюсь…
Мало аргументов?
Теперь я спрашиваю: представьте, что лично вы, в одиночку, собираетесь создать серьезный проект с интересной математикой и сложной бизнес-логикой. Скажите мне, неужели вы станете писать управляющий код на C++? Или, всё же, ограничитесь математическим backend-ом, а контроллеры и UI напишите на чем-то более приемлемом?
Может быть, я что-то серьезно не понимаю…
Chaos_Optima
Эм… зачем, когда есть array. Ну и да внутри vector и int* по большей часть одинаковые.
Лол, как будто в C# и Java это делается по другому)). Ну и да современные компиляторы, когда могут точно определить класс не прыгают по vtbl.
А ещё шаблоны, ссылки, raii, инкапсуляция, лямбды, constexpr, auto, decltype и куча других приятных мелочей.
И правда IDE то у нас нет, приходится всё ручками в консоле компилить, и даже CLion не умет правильно cmake генерировать.
Действительно, ведь так мало средств для документации С++, даже доксигена нет.
Прежде чем писать очевидно непродуманные утверждения, было бы неплохо хотя бы 5 минут посидеть и погуглить эти вопросы, чтобы не нести чушь. Единственное в чём вы оказались правы так это в отсутствии модульности в С++, это и правда серьёзный недостаток, который я надеюсь попытаются решить.
bigfatbrowncat
Я не говорил, что писать в этом случае надо на Java. Я считаю, что в этом случае вполне можно писать на Си.
Есть, конечно, но имеющийся большой проект, например, вы в IDE замучаетесь засовывать. Люди изобрели столько разных сборочных систем для C++, что со всем этим зоопарком просто не разобраться. В данном случае в первую очередь надо думать о legacy, конечно. В вашем собственном проекте вы найдете приемлимую настройку (хотя, привязывать проект к одной IDE — это не комильфо).
Я, честно говоря, не в курсе — а есть IDE, которая из комментариев Doxygen делает контекстную подсказку при вводе кода?
Просто он не является частью стандарта — это лишь костыль, который добавлен к C++ потому, что изначально не было ничего.
Разница между мной и вами в том, что я просто имею более высокую планку требований к инструменту. Вас устраивает поддержка CMake в CLion и вы считаете, что это удобно. Я считаю, что должен либо знать сборочную систему досконально сам, либо она должна быть одна, стандартная и поддерживаться ВЕЗДЕ. А желательно — и то, и другое разом. Я уже 13 лет занимаюсь программированием профессионально и за это время мне порядком надоело иметь дело с недоработанными и неудобными инструментами. Даже если они мощные. Только и всего. К примеру, когда я знаю, что мой проект собирается только в одной конкретной IDE, только под одной ОС или я не знаю, как он собирается, я этого не терплю.
Chaos_Optima
Эм… то есть когда нужен полиморфизм, нужно использовать С? 0_о не думаю что вы могли бы дискредитировать себя сильнее чем сейчас.
0_0 wat? Я ошибся, вы всё таки умудрились дискредитировать себя ещё сильнее.
Поэтому я вам и советовал погуглить прежде чем чушь писать
Да Visual Studio.
эм… ну во первых, зачем эта вещь в стандарте? Ну а во вторых вы считаете что всё что не является частью стандартной библиотеки, а расширяется извне это костыль? 0_о
Нет. Разнится между мной и вами лишь в том что вы не знаете инструмент, который осмеливаетесь критиковать, и что печальнее не хотите его изучить, хотя бы на приемлемом уровне.
0xd34df00d
kdevelop.
Wedmer
Не знаю как у вас, но у нас на нижнем уровне нужен высокий уровень абстракции. Если бы было удобнее написать на C, написал бы на нём. Нижний уровень не требует урезания инструментария, но требует эффективного использования оного. Конечно приходится некоторые библиотечные функции замещать своими, но это касается и C, так как не все функции имеют варианты для целочисленной математики.
Мой большой проект, написанный с нуля, не страдает от того, что обе части написаны на C++. Время компиляции занимает 10 минут для шести целевых платформ. Проблем с порядком библиотек также не замечал.
Я люблю Doxygen. Есть ещё варианты.
Проблем с Autotools и make тоже не вижу. Ещё есть qmake, b2. Вы лучше подумайте, чем и как собирается java runtime.
Если вы не понимаете смысл разделения на заголовочные и компилируемые файлы, то мне вас жаль. Кстати в обоих языках можно жить без заголовочников (кроме системных). Но и в шарпе вы указываете на зависимости.
Не вижу беды в 1000 файлах, если оно так нужно. Ведь для каждого правила сборки писать не надо.
Извините за несистемные ответы, но с мобильного клиента писать не очень удобно.
bigfatbrowncat
Дайте конкретный пример одного неразделимого куска кода, где требуется одновременно ООП, высокий уровень абстракции и максимальная производительность.
Вот это и плохо, что их много. Наличие кучи альтернатив в данном случае означает отсутствие одной, хорошо продуманной и надежной. Которая бы являлась частью стандарта языка.
Подумал. И даже собирал OpenJDK. Жутко он собирается. Сложно, разношерстно и долго. Но, к счастью, человек, пишущий на Java, а не саму Джаву, лишен необходимости заниматься подобными вещами.
А вот Java-код собирается всегда одинаково. Класс файл на один исходник. Файлы лежат по структуре, совпадающей с именами пакетов. Сборка осуществляется путем «компилятор, возьми все эти Java-файлы и все эти jar-библиотеки и сделай мне .class вот сюда».
Тут не с чем разбираться и не в чем путаться. Я могу взять любой Java-проект любой сложности и, если у него нет нативных расширений, собрать его буквально одной командой. И, к счастью, разработчикам обычно не приходит в голову изобрести еще одну САМУЮЛУЧШУЮ систему сборки для Java.
Это, простите, как? Инклюдить один сырец из другого? Быстрая у вас, однако, сборка будет…
И я не вижу. Но этой кучей должно быть легко управлять.
Wedmer
Тот самый проект, про который я писал ближе к стволу дерева. К сожалению, код я вам показать не могу по понятным причинам. Конечно все там можно было написать на голом C, с применением ООП, но проект изначально разрабатывался на C++/boost, да и одним из основных критериев были сжатые сроки.
Честно скажу, что я больше люблю чистый C, но это не мешает мне подбирать инструмент согласно задаче и использовать его так как полагается. Среди инструментов встречаются и C# и другие языки, все зависит от задачи.
Вы забываете, что Java компилируется в платформонезависимый байткод и там только Java. В простых проектах на C или C++ тоже достаточно небольшого Makefile, который вам все соберет на любой платформе одной командой make. Но если начинает пахнуть такими вещами как «Платформозависимый код» или «кросскомпиляция» и кучей зависимостей, то Makefile уже не позволяет все это упростить. Именно поэтому были сделаны Autotools, которые сгенерят configure и некоторые шаблоны к нему, удовлетворяющие среде сборки, а при запуске configure вы уже настроите все под целевую платформу.
Конечно, если зависимостей не очень много и проект не очень крупный, то всё равно можно обойтись и одним Makefile, который будет настраиваться через переменные окружения. Да, приходится платить временем для создания конфигурации управления зависимостями, но производительность конечного продукта этого стоит. Лично я пользуюсь qmake. В заговорили о стандартах? Извините, это универсальные инструменты, которые могут собрать проект, если он написан на всех возможных языках сразу. Если вдруг для C/C++ появится стандартный для всех реализаций инструмент, то эти системы все равно останутся и будут использовать этот новый инструмент.
Это плохая техника, но она применяется ( привет средам типа IAR ). Для компиляции указываются только конечные потребители кода.
Не сложнее, чем такой же кучей на Java.
bigfatbrowncat
Смотрим в исходник IntelliJ IDEA. Там есть Java, Groovy и Kotlin. А еще куча всякой интересной модульности. И всё это собирается прямо из той же самой IDE. Хотя чести ради надо сказать, что из консоли для сборки нужен gradle. Но gradle, в отличие от большинства сборочных систем для C/C++, устроет так: пишешь «вот тут у меня Java, тут Kotlin, тут ресурсы, результат положи, пожалуйста, сюда и запакуй в jar с вот таким именем». И на этом вся конфигурация заканчивается.
Eclipse, правда, собирается с помощью чуть более сложного Maven-а, но он — уже сборочная система, которая считается Enterprise-решением. Им пользуются матёрые джависты в проектах, для которых используют монстров типа Spring Framework.
Кстати, полный объем исходных кодов Eclipse Platform составляет примерно 2 гигабайта. Это именно исходники. Результат (со всеми установленными плагинами) будет мегов четыреста. И этот монстр довольно легко правится, отлаживается и дорабатывается. Такие вот масштабы.
bigfatbrowncat
Думаю, спор надо прекращать.
Я весьма посредственно знаю C++ на уровне крупных Enterprise-проектов.
Вы, по всей видимости, практически не знаете Java.
Каждый любит то, что знает (или, наоборот, знает то, что любит)
Wedmer
Вообще многие ваши высказывания заставляют меня думать, сто C++ и C вы весьма поверхностно знаете.
bigfatbrowncat
C++ я действительно знаю поверхностно. На уровне стандарта 98 года. Qt, Boost не знаю, да. Первый слишком проприетарен, чтобы мне хотелось его использовать для себя, а по работе не приходилось. Второй слишком заморочен.
Учился C++ на работе я, дорабатывая ОС Symbian.
А вот Си знаю вдоль и поперек. Хотя в проектах на «Си с ООП», слава богу, не участвовал уже 4 года. Если быть совсем честным, то обычно я «скоростной» код пишу на C++, ограничивась функциональной парадигмой. То есть использую new/delete вместо malloc/free. Контейнеры использую по минимуму.
0xd34df00d
Ну тут просто нечем крыть, на самом деле.
Пожалуйста, говорите правильно: вероятнее всего, вы используете императивную парадигму, а не функциональную.
bigfatbrowncat
Императивная парадигма от функциональной отличается отсутствием глобального состояния. Пишите «чистые» функции и будет вам счастье. Что я упустил?
0xd34df00d
И локального тоже. Никаких вам static'ов и никаких printf/scanf в чистых функциях. Даже для логгирования (хаскелевский читерский
Debug.Trace
не в счёт).Писать чистые функции можно на ассемблере, но всё-таки лучше, когда язык для этого предназначен.
А ещё лично для меня ФП немыслимо без хорошей, развитой и мощной системы типов.
bigfatbrowncat
Я там, выше по дискуссии, писал, что ФП — вещь тяжело усваиваемая. Я, конечно, как человек, плохо его знающий, не имею права его критиковать, но всё же…
Я точно знаю, что такое состояние системы и откуда оно берется. Философская концепция, лежащая в основе ООП, мне понятна и я, при желании, могу объяснить ее 6-летнему ребенку.
Можете сформулировать, какую часть реальности берет за основу парадигма, в которой запрещено использование локальных переменных?
(к слову, если логгер передавать среди аргументов функции, то логгирование возможно и в ФП-подходе, так? Только, правда, я с детства приучал себя передавать в функции как можно меньше параметров)
То есть без классов? Или вы про какие типы говорите? Что-то я запутался…
0xd34df00d
Я так и не понял, чего там тяжело усваиваемого, ну да ладно. Впрочем, когда я тот же Хаскель в своё время в первый раз ковырял, да, мозги вполне себе ощутимо скрипели. Но какие же ментальные оргазмы этот скрип давал!
Всё — функция в математическом смысле слова.
Да, почти так. Если сильно упрощать, логгером может быть просто список строк, который вы берёте на вход и в который что-то там дописываете.
Да, без классов :) Только не тех, что в C++ или C#, а тех, что классы типов.
В общем, лучше ссылку дам на замечательную книгу по теме: newstar.rinet.ru/~goga/tapl/tapl.pdf
Mrrl
А может быть, продецурную вместо объектно-ориентированной?
0xd34df00d
Я не очень понял вашу корректировку, но да, у меня правильнее было сказать «процедурную» вместо «императивную».
0xd34df00d
Зачем? Если я заранее знаю размер, то
vector::reserve()
меня спасёт.Во-первых, не всегда, современные компиляторы хорошо делают виртуализацию, особенно для final-классов и функций.
Во-вторых, плюсы vtable'ами не ограничиваются. Останутся лямбды, темплейты, обычные классы для RAII, семантика перемещений… Да много чего останется, на самом деле.
Инструменты вообще полезно учить.
Лично я напишу либо всё на плюсах, либо всё на хаскеле (возможно, с биндингами к каким-нибудь сишным blas'ам/lapack'ам через какой-нибудь hmatrix), а от UI постараюсь держаться подальше.
0xd34df00d
Вот на низком уровне мне нужно сделать файлу
mmap()
, гонять потомmemchr
для поиска разделителя и оборачивать токены вboost::string_ref
(эх, где ж мои string views из C++17!). Это уже Си или ещё нет?Что для вас «превращение в Си»?
0xd34df00d
Содержит, на многих уровнях, причём. От «переделать алгоритм так, чтобы он эффективнее работал не с абстрактной машиной Тьюринга, а с имеющимся железом с его особенностями», до «а что ещё тут можно ускорить», вплоть до «если у меня все эти данные в сотню гигов памяти влезут, то всё будет хорошо, и мы сможем делать огого-вещи!».
bigfatbrowncat
С тех пор, как среди сложных задач находится математическое моделирование физики, управление сложными системами и прочий код, который имеет предметную область, требующую нескольких лет в профильном ВУЗе, помимо, собственно, умения программировать.
Вот вам пример сложной программы, например: en.wikipedia.org/wiki/NWChem
Моделирует квантовые состояния и переходы в атомах. В частности. И много чего еще другого.
Думаете, приведете в пример UI сопоставимый по сложности?
А игры, конечно, бывают разные. Бывают и очень непростые алгоритмы, но они очень редки.
Chaos_Optima
То есть для вас сложные задачи это задачи где есть сложная математика, а всё остальное ерунда? По мне так математика это математика, и задачи программистов это в основном задачи алгоритмики. И не уверен насчёт UI но игры по крайней мере ААА находятся чуть ли не на вершине сложности в плане алгоритмов.
Да без проблем, возьмите любой браузер и скажите, есть ли среди них хоть 1 который поддерживает html5 полностью? Не думаю что вы скажите что написать свой браузерный движок это простая задача.
Участвовали ли вы в разработке ААА движков? Скажу вам по секрету, в современных играх не только много сложной математики но и огромное количество сложных архитектурных, и алгоритмических задач.
Конечно в плане математики они сильно уступают NWChem, но это не делает их простыми.
bigfatbrowncat
Давайте не ударяться в софистику. Движок игры — это не UI. UI — это то, во что тыкают кнопками и мышкой. Иногда еще gamepad туда же.
То, что я привел в пример то, что более-менее понимаю, не означает, что я не верю в существование других сложных программ.
Но, еще раз: UI среди них нет.
Я читал код Android Framework и изучал внутреннее устройство виджетов и всего с ними связанного. Это — простой код. Он полон нюансов и мелких решений и оптимизаций, которые делают его непростым в плане развития, но ничего фундаментально сложного там нет.
Я разбирался во фреймворке SWT и много внутри видел. В частности, сделал статическую сборку для встраивания в исполняемые файлы. И там тоже нет ничего сложного.
Что касается браузеров, то там вся сложность — оптимизация. Для того чтобы низкоквалифицированные разработчики могли писать приемлимый по производительности код на языке типа JS, крутые профессионалы должны вложить кучу сил в повышение эффективности рантайма.
Chaos_Optima
Я и не ударяюсь в софистику, это вы написали что игры это просто.
Ещё раз повторяю, браузеры.
Да практически все сложные задачи, это задачи оптимизации. Практически все задачи физики и графики это задачи оптимизации и аппроксимации. Большинство сложных архитектурных и алгоритмических решений, так сложны именно из-за требований оптимизаций.
bigfatbrowncat
Приведите мне пример браузера, написанного на Хаскелле.
Приведите мне пример игры AAA-класса (по вашему же определению), написанной на Хаскелле.
Вы пытались мне доказать, что Хаскелль не менее удобен для этого. Так? Я не вижу, чтобы его кто-то для перечисленных целей использовал.
Когда я говорил о простых задачах, вы прекрасно поняли, что я имею в виду. Я имел в виду задачи, решаемые фреймворком типа Qt и игровыми движками типа Unity.
Я вообще уже не понимаю, о чем этот спор. Изначально я утверждал, что C++ плохо справляется с решением задач в той области, для которой он считается (своими же адептами!) наиболее пригодным.
Chaos_Optima
Нет, я про хаскелль вообще не говорил, я лишь указал вам что вы неправы, говоря что UI и игры это простые задачи.
С++ как раз создан для решения сложных задач по типу браузеров и игр.
Дык и я говорю о том же, фреймворки типа Qt и дивижки типа юнити это не простые задачи.
Я именно это и оспариваю )).
Wedmer
Есть подозрение, что спорить бесполезно.
bigfatbrowncat
Главная проблема «холивара» между адептами C++ и теми, кто относится к нему пренебрежительно, на мой взгляд, состоит в том, что люди, освоившие технологию в совершенстве, потратившие, к тому же, на это много времени (или просто очарованные по молодости ее мощью), искренне считают, что другим, лентяям, достаточно лишь чуть-чуть поднапрячься — и они станут спецы и молодцы.
Я сам в институте почти что боготворил C++ за его красоту и целостность. Раздражение пришло позже, когда я сперва научился писать сложные (больше 5000 строк кода) программы, а потом увидел C# и осознал, сколько усилий в C++ приходится тратить лишь на то, чтобы программа не разваливалась на старте. При этом выигрыш по производительности, которым все адепты размахивают как флагом, составляет считанные проценты, а там, где он существеннее, есть JNI (в Java) и PInvoke (в C#). И программа, написанная на «коктейле» будет работать так же быстро. Только она будет надежнее. И вы ее быстрее сделаете.
0xd34df00d
Для обучения C++ сложнее, вероятно, чем C#.
Я, впрочем, уже лет 12 что-то пописываю на C++ (половину жизни, ух ё), и как-то не могу сказать, что надо прилагать какие-то особые усилия, чтобы что-то не разваливалось.
В том же C# тоже наверняка есть какая-нибудь лажа. Да что там далеко ходить, буквально на днях на глаза попалось это вот. Кое-что там — весьма откровенные придирки, ИМХО (вроде претензций к оператору инкремента), но кое-что ИМХО шедеврально, вроде пункта 1.
Хотя с тем же инкрементом, судя по описанию семантики для переопределённых операторов, там тоже не так всё гладко и интуитивно. Если меня как плюсиста посадить за написание соответствующего C#-кода, я бы наверняка наделал там ошибок.
bigfatbrowncat
>Если меня как плюсиста посадить за написание соответствующего C#-кода, я бы наверняка наделал там ошибок.
Наделали бы несомненно. Но поймали бы их в два счета с помощью имеющегося отладчика и отличных стектрейсов.
Проблема не в сложности C++! Я про сложность не писал ничего. Проблема в том, что он весь состоит из «дырявых» абстракций. Чтобы написать нормальный код на C++ надо сперва самому придумать и выбрать некое подмножество его возможностей, которым вы сами себя ограничите и запомнить набор правил. А потом железно этим правилам следовать. В противном случае у вас будет ошибка в месте кода А, а программа свалится через 10 минут в месте кода Б. И идите — разыскивайте, где вы там испортили что…
0xd34df00d
Если я правильно понял Липперта, то не поймал бы в два счёта с помощью стектрейсов и отладчика просто потому, что оно бы не падало, а просто тихо считало не так.
Это более-менее справедливо для любых языков, потому что любые языки позволяют писать нечитаемый и неподдерживаемый код.
Конечно, в C++ этого больше — прямое следствие необходимости обратной совместимости с C.
В таких случаях я добавляю во флаги сборки
-fsanitize=address
.lair
В C# таких ошибок (именно языковых, а не уровнем выше), на самом деле, очень мало. И даже в приведенном вам списке далеко не все такие.
Но самое главное, что «считает не так» — почти всегда видно сразу, на первом тестировании. А ошибки с копированием объектов и вообще памятью — похуже.
0xd34df00d
Про это я сразу и сказал.
Если взять за правило, например, дебажные сборки собирать с asan'ом, то так же.
На самом деле, я ни разу за свою жизнь не встречался с проблемой с работой с памятью, которая проявляла бы себя сильно потом в дебажных билдах. Как-то относительно везло на локальность.
Chaos_Optima
Интересно узнать каков ваш опыт работы с С++? Просто за те 6 лет что я на нём работаю. у меня никогда небыло трудностей что вы говорите, даже когда только начал. Всегда выбирал методы нужные для решения задачи исходя из задачи, основная идея языка это е использовать того что ненужно, а по вашим словам получается какбудто есть всего 2 возможности, либо используй всё либо используй ничего. Оч странно выглядит.
0serg
Что-то я не видел ни программ C++ где нужно прикладывать массу усилий чтобы все «не развалилось при старте», ни программ на C# где не требовалось бы писать кучу еще более дурацкого boileplate. Весьма показательным, имхо, моментом является то что у нас в проекте когда такие же «светлые головы» решили что вычислительное ядро будет на плюсах, а бизнес-логика и UI — на шарпе, то первым (!) что было сделано на шарпе стало использование какого-то дурацкого IOC фреймворка, который спрятал половину инициализации в непрозрачный third-party бинарник. Затем чуть ли не полгода эти «спецы в C#» делали несложный интерфейс с одним окном для рендеринга 3d-сцены, одним меню и несколькими кнопками в этом окне, т.е. вещь настолько тривиальную, что дальше уже некуда. Дальше были танцы с бубном чтобы подключить к этому окну third-party рендерер. Дальше, естественно, возникли обширные проблемы в общении между плюсовым кодом и шарповым, так что несчастный message box пилили, наверное, месяц. Далее я заметил что добавление банальнейшего элемента в меню требует ручной правки то ли шести то ли восьми (!) файлов. Шаг влево, шаг вправо — и где-то отваливаются ресурсы, из-за чего message-box ы с сообщением об ошибке выглядят абсолютно пустыми. Как сделать банальное диалоговое окно — непонятно вообще, такое впечатление что нужно править вручную машиночитаемую разметку — привет XAML. Слава Богу, что пока что нам это не нужно и это создает со стороны впечатление что UI успешно сделан и прекрасно работает, но я-то знаю, что диалоговые окна которые раньше мог спокойно добавить любой разработчик, сейчас может добавлять только один или два человека в команде, которые шарповый код писали, потому как больше в этом коде сходу разобраться не может никто. В общем, пиздец как он есть. А ведь могли бы сделать на Qt — там все а) работает с полпинка, б) легко читается и правится, в) не требует геморроя с взаимодействием между кодом на шарпе и плюсах. Pinvoke — это хорошо когда надо одну-две функции дергать, а когда на плюсах «живут» не отдельные «оракулы» которые C# дергает в синхронных вызовах а полноценные непрерывно обрабатывающие данные потоки асинхронно от C#, то пробросить (и отладить) нормальный интерфейс довольно непросто.
Это я конечно не к тому чтобы C# плох и виноват во всех этих бедах. Понятно что там программисты на шарповой стороне были очень посредственными и накосячили с архитектурой. Но, блин, вся ж идея с шарпом, по идее, в том что там накосячить должно быть сложно, а сделать правильное решение — легко и просто. И вот этой простоты я что-то абсолютно не наблюдаю. Примерно такую же хрень мог бы нагородить в UI-е любой школьник пишущий на плюсах для MFC. Вероятно шарп это шаг вперед по сравнению с MFC (ибо последний — это вообще запредельное убожество), но по сравнению с Qt, по ощущениям, это два шага назад.
Здесь правда следует сделать поправку на то что плюсовые программы я писал сам, тогда как шарповые смотрел сделанные другими.
lair
Имя, сестра, имя!
(но вообще, конечно, плохая архитектура — она на любом языке плохая архитектура, от нее не защитишься)
0serg
Caliburn.Micro и какую-то хрень LogoFX, но по сути это совершенно неважно.
Моя позиция в том что с хорошей архитектурой и на плюсах и на шарпе будет написан примерно одинаковый код за примерно одинаковое время. У каждого языка будут свои плюшки и минусы (шарп — лучше стандартная библиотека, тогда как к плюсам надо еще подключать что-то, плюсы — быстро работают из коробки), но общая разница будет весьма незначительной. Поэтому когда человек говорит что на шарпе писать сильно проще то у меня возникает отчетливое ощущение что на плюсах он просто писать так и не научился.
Плюсы, имхо, не любят по двум основным причинам
1. Там много legacy-проектов с абсолютно убийственной реализацией тогда как на шарпе проекты в среднем новее. Отсутствие нужды возиться с MFC потому что проект начинали 15 лет назад и теперь без радикальной переделки MFC оттуда не вытравишь любому языку дает много очков вперед :D
2. Плюсы учить дольше и кривая обучения там круче, а ошибки в нем более красочны.
Второй пункт приводит к тому что многие школьники вначале набивают себе шишек в плюсах, и так и не сумев пробиться через этап первоначального обучения уходят в шарп, где учиться проще. Они там допускают те же ошибки что и в плюсах, но поскольку их последствия менее фатальны (особенно в мелких проектах) и обучение идет быстро, то возникает ощущение что эти ошибки связаны с личной неопытностью и вообще не критичны, тогда как в плюсах у них возникает ощущение что ошибки ты будешь совершать всегда и любая из них может убить программу. Начальный сложный период экстраполируется на весь период обучения плюсам, из этого делается вывод что плюсы — это сплошное сражение с языком, а первый пункт подкрепляет эту идею наблюдением «вон смотрите что специалисты наворотили».
Примерно так древние могли бы сравнивать иероглифическую запись с буквенной. Иероглифы как идея проще для понимания, и кажется что их легче учить — выучил еще один иероглиф и уже знаешь больше. А буквы их поначалу учишь, учишь зачем-то без толку — слова-то не получаются. Потом еще их в слоги надо складывать (что, заметьте, нетривиально и требует от детей немалых усилий), и видно что ошибки допустить легко — пропустил букву и уже смысл исказился, орфографические правила какие-то дурацкие существуют. А с иероглифами вроде как проще — картинка она и есть картинка. Но в длинной перспективе, как мы знаем, ситуация-то с иероглифами и буквенной записью вырисовывается иная.
lair
… людьми с одинаковой компетенцией и желательно в вакууме.
Мне до сих пор интересно где-нибудь найти факты о том, сколько нужно времени, чтобы «научиться писать» на C++ и C# «на одном уровне».
Но вообще, из «плюсы учить дольше» неизбежно следует «на шарпе писать проще». Потому что иначе почему учить дольше? И весь ваш дальнейший пример это все и подтверждает.
Грубо говоря, я могу посадить джуниора писать код на C# для реального проекта. Он (код, не джуниор) будет некрасивым, неэффективным, но он будет работать, и если будет ронять систему, то это будет видно сравнительно легко. Я совершенно не уверен, что все то же самое можно сказать о C++.
0xd34df00d
Не следует.
Бульдозером учиться пользоваться дольше, чем лопатой, но ямы копать легче.
lair
(как обычно, метафора больше говорит о желаниях сказавшего, нежели о реальном положении дел).
Во-первых, все зависит от определения «легче». Почему-то не все, кто могут копать лопатой, могут пользоваться бульдозером.
Но во-вторых, даже если принять под «легче» — «производительнее», то как легче выкопать ямку на штык на однодневной палаточной стоянке — лопатой или бульдозером?
(ну и вообще, бульдозером ямы не копают)
0xd34df00d
А почему не все копают — тут-то аналогия и рвётся уже. Впрочем, для аналогий это нормально.
Так и скрипт-однодневку будут писать не на C++. Впрочем, и вряд ли на C#.
0serg
Я пытался показать что более длинное обучение в конце может давать и большую награду (пример с иероглифами). Отдельные буквы учить сложнее чем отдельные иероглифы, но выучив их все скачком оказываешься на плато колоссальных возможностей где можно реализовывать сколь угодно сложные тексты не заучивая новых иероглифов. В терминах «кривой обучения», у алфавита она вначале крутая, а затем практически плоская, тогда как у иероглифов она вроде как пологая, но подъем на ней длится неограниченно долго, а у живых людей еще и тупо упирается в конечную емкость памяти. Выше так же приводили прекрасный пример с бульдозером.
Но тут естественно все зависит от того, что именно Вы хотите получить. Например для передачи примитивных посланий где общее число возможных вариантов сообщения ограничивается буквально десятком-другим иероглифическая запись может быть практичнее алфавита. В качестве живого примера можно вспомнить те же дорожные знаки. Естественно этот пример не делает алфавит менее практичным выбором для чего-то более сложного.
Джуниоры у нас рутинно пишут код и для C++ проекта. Естественно это требует определенного присмотра. Но если объяснить джуниорам несколько простых техник и давать вовремя по рукам при попытке писать свои велосипеды, то у них код будет работать и не будет ронять систему, точно так же как и в шарпе. Я верю что в шарпе без подобного присмотра за джуниорами программа будет падать куда реже чем в плюсах, но, простите, не верю что написанный таким образом код будет легче дебажить или мэйнтэйнить. Будут спагетти с кучей дубликатов и странной логикой и то что оно будет не крэшиться а «просто» выдавать неверные ответы в неожиданных местах, переставать работать при малейших правках и требовать переписывания 90% кода для добавления небольшой фичи окажется слабым утешением.
lair
Очень плохой пример. Алфавит учится проще и быстрее, чем иероглифическое письмо.
Понимаете ли, вторая часть будет одинаковой в любом языке, поэтому выигрыша в первой — достаточно.
0serg
Сложнее, уважаемый. На начальном этапе — сложнее. Потому и придуман он был НАМНОГО позже и потому иероглифическое письмо продолжает применяться в ряде стран, а в ряде языков алфавитная запись имеет значительное сходство с иероглифической (не зная слова невозможно расшифровать его запись). В реальных языках вдобавок куча проблем с исторически обусловленными исключениями, связанными с тем что устный и письменный языки эволюционируют параллельно и не совсем идентично. Иероглифическое письмо «учится» в понимании «написать примитивнейшее сообщение» значительно быстрее.
Нет, т.к. у плюсов есть, хм, свои плюсы :). Просто «какого-то» выигрыша недостаточно, он должен быть достаточно значим. А на практике выигрыш именно в этом конкретном аспекте у шарпа до смешного мал.
lair
У нас с вами разное понимание «учится», отсюда и различие позиций.
bigfatbrowncat
Пример неудачный.
Для того, чтобы выучить C++ и C#, необходимо узнать примерно одинаковое количество примерно равно сложных вещей. В C++ будет небольшой перекос в сторону управления памятью и низкоуровневых понтий, в C# будет страшное слово «рефлексия» и мозгодробительный P/Invoke с нитрыми аннотациями. Везде — свои заморочки.
Я говорю это как человек, который примерно в равной степени владеет обоими языками. Правда-правда. Что бы обо мне тут не говорили за мою позицию выше, я вполне неплохо способен писать и на «крестах».
И именно потому, что я на них пишу, могу сравнивать.
В C++ часто возникает ситуация, когда мелкая ошибка (даже не проектирования, а кодирования) заставляет вас потом сидеть над кодом с микроскопом. Я один пример выше приводил, он не единственный даже в моей небольшой практике. Именно эта особенность языка делает его неудобным.
Мне приходилось ловить странный NullPointerException в Java Enterprise проекте на 1000 с лишним классов. Это было тяжко. Там, кстати, было IoC, которое я сам терпеть ненавижу, но без которого в таком громадном проекте — никуда. И могу сказать, что это было нелегко.
Но поймать мелкую багу в C++ коде подчастую — просто кошмарно трудно.
И если вы не экстрасенс, то никакие знания и квалификация всм не поможет. Ошибки C++ не прощает.
0serg
У нас проект на сотни тысяч строк кода и мне доводилось искать в нем кошмарно сложные ошибки. Да, в плюсах это определенный дзэн, да это может требовать знания ассемблера и прочтения манов по x86. Но нет, это вполне реализуемо и значительно проще чем, к примеру, искать ошибки в плохо написанной многопоточной логике. Дебаггер и логгирование способны творить чудеса, самые мегасложные ошибки при понимании того что и как ты делаешь отлаживаются за один-три дня (один кстати раз нашел баг в компиляторе :D — там, впрочем, все было довольно просто, хотя человек не способный проанализировать дизассемблированный код там бы удавился). Да, у меня ушло 15 лет на то чтобы этому научиться. Но, ёклмн, многопоточные баги (которые вообще не зависят от того, шарп это или плюсы) могут ловиться месяцами (их тупо бывает чудовищно сложно воспроизвести), а еще у нас в проекте есть математические/инженерные проблемы которые решаются ГОДАМИ с довольно незначительным прогрессом несмотря на квалификацию занятых в этом решении людей. Вот это я понимаю — сложность. А самые сложные ошибки характерные чисто для плюсов — это так, рутина на несколько дней, достаточно просто понимать куда надо смотреть и методично проверять что там все «как надо». Примерно как старый телевизор ремонтировать, если Вы еще застали то время когда они из дискретных компонентов набирались :)
Но ладно, это хвастовство и лирика :). С практической точки зрения куда важнее то что по моему опыту отлова подобных проблем, реально это проблема для считанных процентов от общего объема кода. Подавляющее большинство плюсового кода при минимальной самодисциплине пишется так что подобных проблем там не возникнет практически никогда у самого распоследнего джуниора. Главное по рукам надавать вовремя, чтобы отбить идиотские практики изобретения велосипедов и заставить людей пользоваться стандартными (и что, блин, характерно — удобными) вещами. У нас же ведь как бывает? Смотрит человек на задание и вместо того чтобы покопаться в библиотеке или хотя бы спросить старших берет и пишет свой самоходный веник на паровой тяге. В суровом C-style или напротив с кучей плюсовых наворотов а-ля Александреску. И естественно в половине случаев недооценивает сложность задачи. Но стоит начать использовать, блин, банальнейший STL и оказывается что, во-первых, задача в подавляющем большинстве случаев прекрасно решается штатными средствами этой или другой уже имеющейся библиотеки, а во-вторых получившееся решение работает с первого раза, ничего не крэшит, и — сюрприз — еще и делает это зачастую быстрее чем самопальный код :). Да, есть 1-5% кода где нужен хардкор и требуется определенный опыт. Да, это нередко ключевые компоненты на которых держится половина программы. Но, я Вас уверяю, неважно плюсы или шарп — если Вы на эти компоненты посадите джуниоров, то неизбежно огребете проблем по полной программе.
bigfatbrowncat
Для людей вашего уровня язык программирования уже значения не имеет. Речь идет о начинающих и о тех, кто в отрасли 2-3 года.
Я сам, увы, ассемблер не знаю. Но как функции вызываются, знаю более менее, даже ffi, например, щупал. Но ковыряться в этом всём, когда в голове usability — сущий ад.
bigfatbrowncat
Гоните ваших «спецов» взашей. Не за то, что он используют С# (он отличен), а за то, что они его использовать не умеют.
UI любой сложности на Windows Forms делается так быстро и легко, что даже сравнивать не с чем. Я в институте на нем запилил программу мат моделирования со сменными счетными модулями, плагинами и чертовой тучей настроек. При этом счетный код был написан на C++/CLR. И всё это прекрасно связывалось между собой.
А если вы под Mono пишете, то P/Invoke — довольно простая технология. Сложнее, чем JNI, но я не знаю, кем надо быть, чтобы ее не освоить на базовом уровне за неделю.
0serg
Там WPF а не Windows Forms. Типа круче и вообще в духе великих идей Микрософта отдадим UI на аутсорс дизайнерам. Ибо великий WPF отделяет presentation от model, так что наши программисты забабахают model, а дальше WPF ее магическим образом привяжет к UI. Я был сильно против, но именно идея с аутсорсом, насколько я понимаю, в итоге «продала» C# менеджменту. Надо ли упоминать о том что на практике эта идея через полгода благополучно отправилась на свалку?
P/invoke нормально работает когда код можно свести к некой функции «возьми A и Б, посчитай В(А, Б) и верни результат». И то будет совершенно непонятно нафига нужный геморрой (мы же вроде как себе жизнь упростить хотим, а не усложнять на ровном месте?). А у нас код тянет от драйвера в реальном масштабе времени данные с железа и эти данные тут же в реальном времени на десятках потоков и в сотни шагов обрабатывает. И наладить взаимодействие между этим самоуправляющимся кодом самостоятельно генерирующим события и UI через p/invoke, внезапно, оказывается не тривиально.
А так я же сразу написал что проблема не в шарпе, а в программистах. Просто мне регулярно пытаются втюхать что шарп де позволяет получать хорошие результаты с посредственными программистами. А по моей практике — нефига подобного. Крэшиться программа может и будет реже, но мэйнтейнить и дебажить ее будет ровно таким же геморроем.
bigfatbrowncat
Архитектуру MVC придумали не в Microsoft. Ей более 40 лет. И то, что современные UI-фреймворки подталкивают к ее использованию — однозначно хорошо. Я насмотрелся на программы, в которых View и Controller перемешаны друг с другом. И как юзер, и как разработчик.
Ваша неправота в этом вопросе, однако, не отмняет того факта, что MVC должно быть не только в WPF, но и в голове разработчиков (как и ООП и много других умных вещей). В противном случае норального решения не получится.
0serg
Я вовсе не против MVC. Просто там уже не выходит того «легко и просто» о котором Вы пишете и грамотная реализация требует достаточно высокой квалификации и значительной работы и это обесценивает преимущества шарпа — и выигрыш получается (в относительных цифрах) невелик, и джуниора туда уже не поставить. Кроме того этот пример хорошо показывает что на бумаге микрософтовские технологии типа WPF очень удобны и позволяют делать крутые вещи, а как доходит до реализации — половина обещанного не срастается, а вторая половина неудобна.
lair
К счастью, это относится не ко всем технологиям MS.
bigfatbrowncat
Я ничего про «легко и просто» не писал. MVC — это не просто. Но это — чуть ли не единственный подход, который позволяет поддержиывать крупные проекты со сложным интерфейсом.
lair
Справедливости ради, не единственный. Шаблонов отделения логики от представления больше одного, и для разных типов приложения лучше подходят разные из них.
bigfatbrowncat
Вы правы. Я говорю об MVC как о самой идее отделения логики от представления. То есть сам факт, что такое отделение в серьезном проекте необходимо, сомнений не вызывает.
0serg
Эти проблемы (если они есть) тривиально профайлером вылавливаются и устраняются в солидных размеров проекте где работают десятки программистов за пару часов работы одного человека
gandjustas
На моей практике из 10 программистов профайлером умели пользоваться в среднем 4, меньше половины.
0serg
ИМХО на команду из 20 человек за глаза хватит и одного умеющего пользоваться профайлером :)
Но проверить не могу, поскольку в моей компании профайлер худо-бедно умели использовать все
0xd34df00d
Да. Более того, это заразительно.
Вот пришёл я в команду, где профайлерами особо не пользовались. Был у меня предыдущий опыт с Intel VTune, использовал его для своих pet project'ов. На работе его не было, попросил купить лицензии — купили, начал им успешно пользоваться по мере необходимости.
Пара коллег уже обратила внимание, мол, ой, а что это у тебя за такие прикольные таблички, деревья и цветные графики? А покажи! А научи! А давайте ещё лицензий купим! Теперь в команде больше одного человека, пользующегося профайлером :)
(тут ещё, кстати, не могу не вспомнить замечательных авторов PVS-Studio с их замечательной политикой выдачи беток и всего прочего только при разговоре как с представителем компании)
maaGames
> Поэтому vector использует динамическую память для хранения массива и должен честно проверять выход за границы.
В релизе vector[] НЕ проверяет выход за границы. Если хочется «честногО» сравнения, используйте vector.at()
>код, который можно встретить в программах с вероятностью больше статистической погрешности. Для примера буду использовать ту же саму пузырьковую сортировку массива.
Типовой код. Бабл сорт. Типовой. Большая вероятность… Какой смысл сравнивать производительность типовых лабораторных работ первокурсников?
gandjustas
Замените бабл сорт на свертку. По большому счету ничего не изменится.
Однако…
maaGames
> Те кто знают (или думают что знают) C++
Классно вы себя приложили.) Различие между [] и at именно в том, что проверка выхода за границы в релизе есть только у метода at. Встречал кривые реализации, где скобки даже в дебаге границы не проверяли. Именно поэтому между массивом и vector в релизе разница околонулевая (на усмотрение компилятора).
gandjustas
Я не сравнивал детали (мне они не интересны), я сравнивал код, который может написать человек. При прочих равных 9 из 10 напишут [], а не at. То что [] позволяет отстрелить себе ноги — это прекрасно.
maaGames
Вот и я о том же! Сперва один сравнивает языки неадекватным способом. Затем другой, пытается доказать неправоту первого. При этом оба не знают тонкостей «противного» им языка.
Допустим, что сравниваем производительность пузырька, мне не жалко. Но! Программу на С++ должен писать хорошо разбирающийся в С++ программист, а на C# — разбирающийся в C#. Без обид, но С++ вы если и знаете, то не сильно плотно. А я не смогу написать максимально эффективно на C#, только если случайно совпадёт. Т.е. все эти три статьи о «сравнении» языков — пердёж в лужу (извините, не ругайте).
C-style нужно делать через указатели. Почти 100% гарантия того, что будет самый эффективный вариант. Если компилятор индексированный доступ сам не смог в указатели переделать.
gandjustas
Сделайте форк, улучшите код. Вам кто мешает?
Вы же хорошо разбираетесь в C++?
maydjin
Ну, и есть целый ряд задач, где 10мс вполне себе штука за которую можно и инструмент сменить :)
gandjustas
Например?
maydjin
Любая графика, ибо смотрит на неё человек а в секунде не так уж и много милисекунд.
Или, внезапно, любой процессинг где важнен лэтенси и/или трупут. Те же трейдинговые системы, сетевые фс, вебсервера, всё что безкровно плохо масштабируется горизонтально.
vanxant
Перефразируя, трупут это тупо деньги.
А вот лэтенси это искусство.
gandjustas
Правильно все, но C++ и вообще скорость кода не при чем в большинстве случаев.
Графика давно делается на видеокартах и руками никто пишет текстурирование. Физика в играх — векторные вычисления, для них есть SIMD в .NET 4.6 (в этом сравнении нет).
Сетевые фс и прочие серверы упираются в IO, поэтому им скорость кода до одного места.
Трейдинговые системы, кроме HFT, не требуют реакции в 10мс. Ордер от обычного терминала исполняется минутами,10мс ничего не решат.
maydjin
По большому счёту — согласен.
Однако, многие готовы перестраховаться и переплатить (сейчас их всё меньше). Ибо, если ты не можешь масштабироваться вширь, то экономить на вертикальном масштабировании как минимум глупо, зато делать это железом чаще дешевле.
Либо, как указал товаришь ниже — системы, которые в принципе сами по себе производительны на грани того, что в них хотят пропихнуть за единицу времени.
Тобишь, если есть риск просто не уместиться на заданной платформе, то тоже разумно выбрать инструмент, который максимально приближает вас к этой цели.
Хотя если речь не идёт о размере (физическом железки), обычно такая экономия тоже на спичках.
Ну и больше субьективное мнение — при должном размере и скорости кода, а так же при грамотном ручном управлении памятью, таки общий прогресс системы всё таки будет выше за заданный квант времени (просто не всегда бывает нужен выше). Реже будут блокироваться шины, сбрасываться кэш, происходить подкачка(не дай боже). Но всё это страшно дорого, и может оказаться выгодно только когда заходит речь об утилизации железа или действительно имеется такой поток данных/сложность вычислений что бы загрузить проц.
Другое дело, что большинство систем тупо спят оставшееся время после того как выполнили свой код, чему опять же свидетельствуе опыт нижеотписавшегося товарища.
Из областей где ещё может понадобиться быстрый код это например парсера — что бы упереться в i/o надо всё таки сначала успеть всё разгрести и подготовить в нужном формате, а так же дать время выполниться полезной нагрузке. И криптография, хотя там обычно всё таки C а не кресты, да и многие вещи наверное тоже можно порешать аппаратно.
gandjustas
На моей практике все упиралось в диски, потом в сеть, потом в объем памяти, а лишь в последнюю очередь в процессор.
А кто спорит? Только это вы не про C++ пишите. Он, к сожалению, делает довольно много работы (подсчет ссылок, вызов деструкторов, выделение\освобождение памяти). Это на голом C или C_с_классами можно все контролировать, но и вероятность ошибиться на голом C в разы выше.
maydjin
О_О. Подсчёт ссылок в c++? Походу, я чего то не знаю…
Нет, ну есть конечно всякие умные указатели, однако без них кресты не прям таки сразу превращаются в C_с_чем бы то ни было.
>да и как связаны 10мс и масштабируемость
Вот стоит у вас задача, за 100 мс обработать 10 запросов. А потом появилась задача обработать 1000 запросов. За 10 мс каждый. Потом ещё, потом ваша железяка за 3ккк не потянула. На ipc при горизонтальном масштабировании — тратиться 10мс. Вы уже не можете утверждать что можете масштабироваться горизонтально бескровно. Если речь идёт о любом маломальском реалтайме (что то нету в CS 500 на 500 баталий, хотя казалось бы мультикаст всех спасёт), то вы в принципе не можете масштабироваться горизонтально ибо на ipc у вас уходит больше времени, чем нужно на обработку запроса (надо же ещё пэйлоад уместить).
Выход — масштабироваться вертикально. Покупать 10G сетевухи, крутую мать, кидать на неё процов, памяти и производить ipc за более короткое время, так как оно перестаёт быть собственно ipc, а становиться чтением записью памяти. А так же утилизировать это всё максимально экономно.
P.S. CS — чисто первое что в голову пришло, но факт что в тех же сетевых игрушках не просто так мир разбит на зоны, а комнаты имеют вместимость.
gandjustas
Смартпоинтерами не пользуетесь?
Именно сразу и превращаются. По большому счету в чем разница между new\delete и malloc\free, если каждый руками выписывать?
Когда запрос приходит по сети, то большая часть времени тратится на сетевой IO. Очевидно что в этом случае надо не ждать пока весь запрос будет получен, а начинать обрабатывать пока клиент шлет байты. Например в RavenDB (NoSQL база на .NET) чуваки сделали так, что умудряются писать на диск пока клиент отправляет данные.
Когда сможете повторить такое на плюсах — приходите поговорить о массовой обработке запросов.
encyclopedist
unique_ptr не содержит никакого подсчёта ссылок и не даёт накладных расходов. shared_ptr это специальная вещь, которой пользуются только когда он действительно нужен.
— Ни во что не превращается. Отсутствие new/delete никак не связано с умными указателями. Я вот пишу код без new и без умных указателей. Да и по большей части без указателей вообще.
Вы в комментариях раз за разом доказываете, что ваше знание C++ довольно поверхностное и основано на мифах.
gandjustas
unique_ptr нельзя передать просто в метод. А если метод может сделать что угодно, то ничего кроме shared_ptr вообще передавать нельзя.
Искренне рад, что у вас такие простые задачи.
Pushkoff
> unique_ptr нельзя передать просто в метод.
можно передать по ссылке
encyclopedist
— остановитесь пожалуйста писать бред.
unique_ptr можно передавать в метод. Это будет означать передачу владения. Если же владение передавать не надо, то передаётся обычный указатель с помощью p.get(). Также можно передать ссылку на объект.
Если же ваш метод делает вообще все что угодно, то я даже не знаю как это комментировать. Тут вам ничего не поможет. Даже C#.
Объекты хранятся в контейнерах, доступ осуществляется по итераторам. Стандартная библиотека все делает за меня. И от сложности задачи это слабо зависит. Конечно есть случаи, когда нужны new, указатели и умные указатели, но это совсем не основной стиль использования современного C++. new при этом обычно размещается в конструкторе некоего класса, delete в десрукторе, и дальше работает RAII.
encyclopedist
gandjustas
Не только слышал, но и видел, поэтому и говорю — приходите как сможете повторить.
Chaos_Optima
То есть для вас С++ это исключительно использование умных указателей? Шаблоны, RAII, ООП, оказывается это всё ерунда, о чём я думал ((. Я например когда писал свой парсер умные указатели не использовал, я вообще писал свой аллокатор, который освобождал потом всю память махом когда он переставала быть нужной. Чем дальше вы пишите тем яснее становится что навыки использования С++ у вас совсем скудные.
gandjustas
Интересно, как вы собрались RAII делать без умных указателей?
ООП — это реально ерунда, stl вообще без ООП сделан и выглядит гораздо лучше, чем если бы его сделали на ООП.
Шаблоны — хорошая штука для библиотечного кода, но для прикладных задач вполне можно без них обойтись.
Chaos_Optima
XDDD Пожалуй в данном случае даже отвечать ненужно ибо вы сами себе могилу выкопали, но всё таки отвечу.
RAII работает не благодаря умным указателям, это умные указатели работают благодаря RAII. RAII связан с вызовом деструктора.
Например при удалении такого класса
У всех его членов вызовется деструктор без какого либо явного вмешательства пользователя.
Это как бы неправда любой контейнер в stl имеет определённую иерархию (как минимум в реализации от MS)
Да но тогда придётся отказаться от stl или использование шаблонов и их написание это взаимоисключающие вещи? И опять же, да для проектов Hello world они и правда ненужны, но вот в реальности всё не так радужно.
gandjustas
ОК, вот у вас есть A. И вы хотите A передать в функцию, которая может делать что угодно, вплоть до того, что сохранить A в глобальной переменной. Привет умные указатели ;)
Это детали реализации, сравни с контейнерной библиотекой C#\Java, там из всех ушей ООП торчит/То что вы можете в подмножестве кода обойтись без умных указателей не означает, что можно писать идиоматичный C++ код вообще без них.
В реальности прикладной код (который решает бизнес-задачу) почти не требует шаблонов. Прикладной код конкретный, а не обобщенный.
Вот в Go выкрутились, там нет шаблонов и генериков, тем не менее массивы и коллекции представляют из себя обобщенные типы и есть обобщенные функции. Свои такие функции создать нельзя. Это напрягает, но тем не менее писать прикладной код не мешает.
Не спорю при этом, что для библиотечного кода прекрасно подходят шаблоны и именно из за шаблонов stl крут.
Chaos_Optima
Да сколько угодно, при наличии прямых рук умные указатели вообще не используются. Например когда я работал в геймдеве под мобильные платформы, мы вообще не использовали умных указателей, стараясь выжать максимум производительности. Ну и да ваш пример про А слишком абстрактный, в реальности такой пример редко встретишь.
Да )) и эта деталь означает что stl использует ООП
Что значит почти? То что вы не писали шаблоны не значит, что почти не требует, это всего лишь значит что у вас мало опыта, либо область ваших задач не требует использование шаблонов. У меня например ещё не было ни одного проекта где бы я не писал шаблоны.
gandjustas
Когда в руках молоток все кажется гвоздями.
Попробуйте другие языки, Go например. Поймете, что и без написания шаблонов можно прожить.
0xd34df00d
Но зачем без них проживать, если с ними удобнее?
А прожить можно и без C вообще, на голом ассемблере писать.
Или тумблерами программы сразу в бинарных кодах вводить. Можно же.
maydjin
Разница — в RAII, которое не строго равно умным указателям. Умные указатели это ширпотреб для случаев когда важнее внятно записать бизнес. Однако, есть ещё размещение например, где RAII также остаётся в силе.
Не просто так эту концепцию таки стали потихоньку пилить и в .net и в java, посредством всяких with и lock.
shared_ptr, который вы вероятно имели ввиду когда говорили про подсчёт ссылкок, в реальной жизни нужен не так уж и часто, по той простой причине, что при грамотной архитектуре практически всегда у обьекта есть владелец и в большинстве случаев он один. Максимум что может являться частым случаем — передача владения.
> Когда запрос приходит по сети, то большая часть времени тратится на сетевой IO.
Зависит исключительно от типа полезной нагрузки. Выполнять i/o одновременно с обработкой данных это квинтэссенция современного серверного ПО. Так делают хоть на эрланге хоть на плюсах, никакого рокетсайнс там нет.
gandjustas
Это в эрланге нет, а в плюсах — еще какой. Даже нагуглить такой код на плюсах проблематично.
Это зависит от задачи. Чем сложнее задача, тем сложнее все свести к древовидному владению, тем больше расходы на поддержку указателей и меньше выйгрыша от использования C++.Например в случае визуального редактора с undo-redo и нетривиальными объектами это уже крайне сложно сделать. Я сам такое писал примерно в 2006 году, и не зная буста и начитавшись александреску я изобретал свои smart_ptr для этой задачи.
maydjin
Сложные задачи — надо декомпозировать. А над архитектурой — задумываться. Подсчёт ссылок был сделан для того, что бы сократить трудозатраты на эти задачи, особенно в контексте потоков. Для этого же он служит и в плюсах, однако, тут никто не заставляет использовать его онли.
0xd34df00d
В случае визуального редактора и undo-redo время нажатия Ctrl+Z пользователем будет на многие порядки больше оверхеда от shared_ptr.
А я вот парсеры натуральных языков пишу, и как-то умудряюсь во времена масштаба десятков-сотен микросекунд (микро, Карл!) вместить разбор поступающего запроса, спеллчек (умный, с language model'ами на десятки гигабайт), прогон собственно парсинга и ранжирование.
На плюсах, да.
maaGames
Так вы же читерите там! Ищете словоформы по хэшам бинарным поиском, а надо строго линейным! Дважды, чтобы наверняка! Иначе не честно.
maaGames
Если вы теряете ВСЮ производительность из-за подсчёта ссылок, то вы явно делаете что-то не так. Самый простой вариант — сделать интрузивный подсчёт ссылок, тогда он получается практически бесплатный, потому что счётчик находится в объекте, а не хранится где-то отдельно и нет лишней беготни по памяти — минимум кэш миссов.
Ничего личного, но в 2006 году знаний С++ у вас было меньше, чем сейчас, поэтому что вы там натворили — одному Богу известно. А говнокод может тормозить по любому поводу.
Chaos_Optima
Очень забавно слышать таки голословные утверждения в сторону геймдева, графика — да делается на видеокарте, только вот организация ресурсов, и объектов на вывод и многое другое делается не на видюхе. Октри, ии, частицы, батчинг, анимация (вы даже не представляете как сильно нужно извращаться чтобы вывести с хорошим фпс хотя бы 150 моделей). Да и в физике кроме векторных вычислений используется огромное количество алгоритмических оптимизаций. И это мы ещё не дошли до мобильных платформ. Так что попрошу не делать голословные заявления в теме в которой не разбираетесь, или вы думаете что в игровых компаниях работают идиоты которые используют С++ чисто из принципа?
gandjustas
Тем не менее огромное количество игр делается на Unity, который, внимание, C#. Так что непонятно что вы пытаетесь сказать. Исходный посыл был в том, что ради 10мс стоит заниматься C++, я лишь сказал что в играх не часто встречается эта проблема.
Самое смешное, что для высокоуровневой логики в играх используются скриптовые языки, которые иногда тормозят дико. Почему бы их не заменить на C++? Оказывается C++ слишком сложен для этих задач. Или вы думаете в игровых компаниях работают идиоты?
Chaos_Optima
Который, внимание, С++. С# используется лишь для скриптования игровой логики, упс.
Именно что для игровой логики которая является наименьшей проблемой для производительности, ваш кэп. Ну и да, чаще всего стараются выбрать наиболее быстрые скриптовые языки, lua, AS, JS/
gandjustas
Как же люди целые игры пишут только на C#, они наверное не знают что там надо C++ использовать.
Она является наибольшей. Помню игру СТАЛКЕР, у которой логика на Lua. Бегаешь по полям — 30+ fps, появляются 3-4 противника (не в кадре, а просто в окружении) — 12 fps. Это ты называешь наименьшей проблемой? Или цивилизация, в которой противники ходят минутами это меньшая проблема?
Chaos_Optima
Я говорил про сам движок, который написан на С++, а не про пользователей этого движка. Повторюсь игровая логика зачастую является наименьшей проблемой для производительности.
Да это наименьшая проблема, я не знаю как устроен движок для сталкера, но склонен думать что это проблема не скриптового языка а организации архитектуры в целом.
gandjustas
Да хоть на ассемблере, без разницы.
А пользователи движка не программируют графику и физику на C#? Или они там магически сами считаются?
От повторения оно правдой не станет. В сталкере проблема была именно в скриптовой логике, там десятки тысяч строк, поведение персонажей заскриптовано, поэтому при нескольких персонажах на сцене все тормозит.
В тупых коридорных шутерах поведение врагов примитивное и там скрипты действительно не проблема. А в игрушках типа цивилизации, где вся логика на python, скорость скриптов — основная проблема. Тем не менее на C++ не переписывают что-то.
Ogra
Графику и физику — нет. Пользователи лишь отдают движку граф сцены, а тот, написанный на С++, отрисовывает все как надо. Физический движок тоже на С++ написан. То, что в C# проброшены интерфейсы к физике, ничего не меняет.
gandjustas
Ага, граф сцены из воздуха получается. Ну мы уже поняли, что создатели игр на Unity вообще ничего не длают, за что им только деньги платят…
Ogra
Один объект графа сцены может быть моделью на десять тысяч полигонов. C# работает с одним объектом, С++ с буфером на несколько тысяч вершин. С физикой все еще круче.
Создателям игр на Юнити платят за создание игр, а не за работу над движком.
Chaos_Optima
Нет.
Да, хоть и не магически.
Вы перед профайлером свечку держали? Откуда такая уверенность?
gandjustas
С саппортом общался. Я как раз думал что тормозит из-за графики. Понижал настройки — все равно тормозит. Потом спросил что за фигня, говорят процессор слабый и не справляется с AI. Потом выяснил что весь этот AI сделан скриптами в Lua.
Chaos_Optima
Ai крайне обширен. Если делать на скриптах только поведенческие алгоритмы то ничего тормозить не должно. А если писать всю систему ии вместе с поиском пути, машиной состояний, или что они использовали. То не стоит удивляться, что всё тормозит ибо эту часть и выносят на С++.
Chaos_Optima
Кстати ещё дополню.
Расскажите это пожалуйста людям которые используют Unreal engine для разработок AAA игр. По видимому они зря используют С++ в качестве основного языка для кодирования игровой логики.
gandjustas
Может и не зря, я не знаю всех обстоятельств. Но есть куча игр со скриптами (Lua, Python) ты считаешь что они зря это делают и надо все на C++ переписать?
ZakharS
Хм… А мы тут пытаемся на несколько десятков микросекунд выиграть… Да, это HFT.
Wedmer
Система распознавания человека на путях в метро. За 100ms на дохлом cortex-a8 надо кучу аналитики провести. А ведь еще приходит куча данных от других датчиков.
Goodkat
Для ваших задач, небось, и С++ покажется слишком медленным, и будете писать на чистом С :)
Wedmer
На данный момент все устраивает. Даже есть большой запас по производительности. Код, который отъедал 100% процессорного времени, теперь редко достигает 0,1% на тех же задачах)
wAngel
Скажите, почему в примерах для C# нет «прогрева» JIT?
gandjustas
Есть, смотрите исходник на github.
encyclopedist
Кстати, если уж вы за идиоматичность, то в коде на C++ стоит использовать std::swap(m[i], m[j])
Door
к сожалению, речь о идиоматичности не идёт — пузырьковую сортировку, написанную ручками, кто использует?
gandjustas
Не путай идиоматичность кода и типичность задачи. Задачи разные, а код — почти одинаковый. В этом и есть идиоматичность. Обработки массивов сведутся в выделению памяти, циклам, получению и записи элементов по индексу.
gandjustas
Хотел и так написать, но аналога для C#, даже близкого, нету.
gandjustas
Сделал swap и однозначно скажу — не надо его использовать. В дебаге тормозит так, что работать невозможно.
withkittens
Тогда и итераторы в stl на помойку! В дебаге тормозят так, что аж мама не горюй ;)
0xd34df00d
Идиоматично в C++ делать
stavinsky
Кстати вопрос: а корректно ли использовать измерение времени выполнения тем же языком который мы поставили под микроскоп? Может все же внешнюю утилиту для этого использовать? И одну на оба теста
Mogost
Тогда это не позволит оценить такие моменты, как прогрев C# кода.
abby
Во-первых, напомнило python, где тут же налетят и предложат десять способов написать правильный for, да еще и для разных версий.
По статье:
maaGames
Тут сортировка меняется на обратную, так что циклы кружатся не в холостую. С точки зрения пузырька — как раз самый плохой случай. Хотя, для него любой случай — самый плохой.
u_story
А какой JIT использовался? В 4.6 по дефолту ryujit уже стоит?
Можете попробовать прогнать тесты с другим старым\новым JIT?
gandjustas
Насколько я понял в .NET 4.6 x64 по дефолту ryujit. blogs.msdn.com/b/clrcodegeneration/archive/2015/05/27/ryujit-and-net-4-6.aspx
Скорее всего под него есть свои хитрости оптимизации, но я не вникал.
JIghtuse
Маловероятно. В каждом проекте узким горлышком будет своё место, и без профилирования делать такие выводы не стоит. Забыли поставить амперсанд — профайлер вам подскажет. Соглашусь всё же, что это ближе к коду на C#. Но на мой взгляд обе статьи занимаются странным. В любом случае сравнивается две конкретные реализации языков, имеющие нечто общее, но в целом принципиально по-разному устроенные. Что, если взять другую платформу? Другой компилятор и набор опций? Заглянуть наконец-то в профайлер?
gandjustas
Обычно узкое место не в коде, а в алгоритме или периферийных устройствах. Поэтому горлышки будут как на C#, так и на C++.
0xd34df00d
Вот нужно мне было загружать файл с таблицей вида
слово1\tслово2\tчисло
. Изначально там былstd::istringstream
. Тормозит, скажем, 95 секунд грузится на 170 миллионах строк.Выкинул
istringstream
, взял вызовmmap()
, взял вместо строкboost::string_ref
, написал свой парсер слов и чисел (благо, формат весьма жёсткий) — опа, 50 секунд. Дальше ускориться я уже не осилил :(evocatus
Скажите, а Вы убедились, что во всех примерах процессор работает на одинаковой частоте?
gandjustas
Я запускал две программы одновременно порядка 20 раз.
Bas1l
В обеих статьях, мне кажется, упускают как минимум garbage collection. Даже если весь ассемблерный код, кроме деаллокации, будет идентичным, C# будет медленнее (тестов у меня, конечно, нет, и все зависит от «паттернов» аллокации-деаллокации, но тем не менее). Кроме того, задержки будут происходить в непредсказуемое время.
lair
Вот именно, что все зависит от паттернов. Представьте себе, что у вас веб-сервер, и освобождение ресурсов происходит уже после того, как данные возвращены клиенту — это освобождение влияет на производительность?
JediPhilosopher
На ответ конкретно этому клиенту может и не повлияет. А вот следующему, на ответ которому уже не хватит памяти и придется делать GC и отдуваться сразу за всех — очень даже может быть.
И я реально с таким сталкивался, приходилось оптимизировать аллокации и ковырять настройки GC.
lair
Ситуаций «не хватило памяти — решили сделать GC», конечно, лучше избегать.
Я не говорю, что управляемая память не несет издержек, просто иногда эти издержки можно разумным образом вынести за пределы пользовательской проблемы.
lockywolf
Для плюсов тоже есть сборщики мусора, если хочется.
Bas1l
Лично мне не хочется, как раз наоборот.
FiresShadow
Есть способ и в С++ мусор автоматически собирать, но при его использования грань между С++ и С# становится весьма условной.
VenomBlood
Но C# может быть и быстрее, все зависит от конкретного кода, т.к. выделение малого объекта на куче по скорости идентично выделению объекта на стеке, в языках без сборки мусора куча устроена по другому и выделение занимает больше времени.
bigfatbrowncat
Хоть я и не сторонник C++ в сложных программах, но вынужден признать, что работа с памятью — слабое место мусоросборных языков. И в первую очередь, сложность в том, что GC почти неуправляем и труднопредсказуем.
VenomBlood
Еще раз перечитайте. В C# выделение объекта на куче проще и быстрее, что делает его в определенных задачах быстрее. Но конкретно судить о языке по паре синтетических тестов все равно несостоятельно.
GrigoryPerepechko
А в плюсах можно написать линейный аллокатор, и аллокация и сборка будет раз в 10 быстрее чем на куче.
И пишут.
gandjustas
Если обратишь внимание, то код тестов выполняется 100 раз, выделяя в каждом 40000 байт (+оверхед), это выше границы поколения, поэтому затраты на сборку мусора включены в измерения.
За счет того, что GC убирает мусор большими объемами, он гораздо эффективнее чем одиночные деаллокации в C++.
Например, если прогнать просто цикл с new\delete в C++, то он окажется гораздо медленнее цикла с new в C#.
Bas1l
Честно говоря, в первый раз слышу такую идею. Даже в Рихтере (CLR via C# который) про такое не пишут, кажется. Я не совсем понимаю, почему GC будет быстрее, чем С++ деаллокация, если собирает большими объемами. Вот почему будет медленее легко можно сказать: нужно остановить рабочий поток, запустить GC, протрекать, на какие объекты есть ссылки из стека (пройти весь стек, дерево объектов), записать адреса объектов в очередь freachable, и т.п. Я согласен, что если делать GC редко и удалять много, то вроде бы будет быстрее, чем если делать GC часто и удалять мало. Но это все равно будет медленнее, чем детерминистическая деаллокация С++, при которой просто вызоваются деструкторы в нужных местах (которые все равно будут вызываться при GC). Короче, деструкторы всегда в сумме займут одно и то же время, а вот оверхед есть только в GC.
Ogra
Меньше фрагментация кучи, кстати.
Например, у нас есть три объекта в памяти, А, Б, В.
++++++АААА, ББ, ВВВВВВ+++++++ (+ — память занятая другими объектами)
Удаляем объект А:
++++++----, ББ, ВВВВВВ+++++++ Надо добавить освободившуюся память в список свободных блоков. +1 блок
Удаляем объект В:
++++++----, ББ,------+++++++ Надо добавить освободившуюся память в список свободных блоков. + 2 блока
Удаляем объект Б:
++++++----,--,------++++++++ 3 блока
++++++------------+++++++ или дефрагментация
GC же может пройтись по всем трем объектам, и потратить время только на добавление одного свободного блока, и итоговый оверхед будет меньше.
RPG
А вообще следует закругляться со сравнением языков X vs Y на тривиальных задачах, особенно сравнивая C++ с Язык Y (всегда найдётся человек, который перепишет ваш код на SSE4.2+OMP, и сравнение завершится не в пользу последнего). Не верите — посмотрите на очень забавный сайт, где участники буквально соревнуются в искусном владении машинным кодом. Сейчас всех волнует не столько скорость, сколько потребление ресурсов и отзывчивость, если уж C++-разработчики добиваются весьма впечатлительных результатов, было бы любопытно посмотреть на языки с автоматическим управлением памятью в аналогичных условиях…
gandjustas
Мне неинтересно кто и как извращается. Я в свое время тоже извращался, упаковывая программы с окнами и IO в сотни байт на assembler, но вырос из этого возраста.
Еще раз повторю: мне интересно было сравнить быстродействие двух кусков кода, которые очень похожи на те, которые напишут 9 из 10 программистов.
Reeze
Было бы интересно увидеть сравнение при .NET Native
gandjustas
Сделайте форк, добавьте тест в universal app и .NET Native.
gandjustas
Сделал тест для Native
ilyanik
Я тут по работе написал logging library на C++, со всевозможными блекджеками и шлюхами. По ходу дела обнаружил spdlog, и оптимизировал, пока не добился аналогичной производительности (около 3 миллионов форматированных сообщений в секунду). А потом сделал wrapper для C# (который делает только меньшую часть форматирования, и вызывает C++ код). Производительность C# меньше в 6 раз.
withkittens
Это вы от Marshalling хотите быстродействия?
0serg
Если в C++ коде мне встречается настолько простой кусок кода, который при этом жрет приличное количество ресурсов, то я его в плюсах могу скомпилировать ICC или ручками вписать интринсики для SSE/AVX, благо что на плюсах это несложно. Обычно код удается подобным образом ускорить вдвое (но с большим разбросом, где-то и вчетверо, где-то на 20%). В шарпе подобное, насколько я понимаю, невозможно. На тривиальном коде до оптимизации, наверное шарп действительно благодаря JIT-у после «прогрева» будет медленнее «всего» на 20%, на оптимизированном вручную — сомневаюсь.
Но у меня в проектах подобные тривиальные куски кода где не требуется работа с памятью — редкость. Практически весь реальный код требует аллокации и освобождения памяти и там, сдается мне, шарпу резко станет хуже. Из интересного, к примеру, несколько дней возился с кодом который реализовывал многомерную аппроксимацию некоторой функции разложением в ряд Тейлора второй степени. Представьте себе квадратичный многочлен от N переменных, для которого сложение определено тривиальным образом, а умножение отбрасывает степени полинома выше второй. Математика там была тривиальной, а вот работа с памятью требовала эффективно аллоцировать и деаллоцировать кучу мелких объектов и объединять их затем в объекты покрупнее. Ну и взять N=600, несколько десятков тысяч операций с полиномами, и все это должно работать в рантайме, ибо является частью 3D-сканера который на лету должен обработать входящий поток данных. Ухайдокал рабочую неделю, но ускорил код вчетверо — не прибегая, заметьте, ни к какому «C-style» программированию с голыми указателями. С трудом представляю как на этой же задаче работал бы шарп.
И, все же в исходной задаче вызывает определенные сомнения методика работы с памятью. Я не спец по шарпу, поэтому мне сложно судить насколько адекватен вызов GC.collect в конце программы. Но на первый взгляд для сравнения это совершенно адекватный ход, поскольку финальное состояние программы в противном случае будет разным, ибо C++ код в конце выполнения никому ничего не должен, а шарповый имеет кучу несобранного мусора, на освобождение которого потом — уже вне измеряемого цикла — будет потрачено сколько-то времени. Не получается ли так, что Вы банально вынесли работу с GC за пределы измерения и, условно говоря, если Ваш цикл прокрутить не 100 раз, а 100.000 раз, то поначалу все будет хорошо, а на какой-нибудь 5.643-й итерации отжираемая небольшими кусочками память таки закончится и шарпу резко и внезапно поплохеет? В силу плохого понимания того почему GC.collect вдруг «портит оптимизацию» я допускаю что ошибаюсь и Ваш подход верен, но было бы как бы неплохо Ваш подход как-то обосновать.
gandjustas
Как раз наоборот, хуже становится C++. GC прекрасно справляется с такими случаями когда выделяется много памяти, а потом она вся становится мусором, особенно когда дело происходит в одном потоке.
Если объекты короткоживущие, то C# сильно быстрее C++ будет. У C++ встроенный аллокатор медленный, в каждой второй advanced c++ книге выдумывают свой аллокатор по этому поводу.
Если мелкие объекты должгоживущие, то нужно в C# извращаться, создавая свои пулы из массивов структур.
0serg
Я не вижу проблем заменить аллокаторы, это несложно и в наших проектах там где это актуально используется.
Но здесь размер объекта не фиксирован и может сильно варьироваться, поэтому простой подход не прокатывает
И не надо катить бочку на плюсовый аллокатор. Штатный аллокатор (а точнее malloc) — это маленький шедевр программирования и он _очень_ эффективен для своей универсальности. Понимаете, проблемы заменить аллокатор, как таковой, в плюсах нет и если бы кто-то умел решать ту же задачу намного быстрее — все бы давно этим альтернативным аллокатором пользовались. Но на практике этого нет. Кастомные аллокаторы работают быстрее не потому что штатный медленный, а потому что они решают более простую задачу и благодаря учету ее специфики могут работать быстрее. Из более-менее универсальных аллокаторов которые при этом еще и быстрые мне вспоминается только интеловский аллокатор — он быстрее для многопоточных приложений, но расходует больше памяти.
gandjustas
Штатный аллокатор — страшный тормоз. Именно поэтому его заменяют
Если бы это было не так, то никто бы не занимался подменой аллокаторов.
0serg
Повторяю для тех кто не в состоянии прочитать комментарий еще раз: специализированные версии аллокаторов за счет использования специфики задачи или каких-то дополнительных компромиссов (например менее эффективного заполнения памяти) можно сделать быстрее. В основном игра стоит свеч только для тривиального случая «много одинаковых объектов». Но даже там какого-то невероятного прироста скорости нет.
gandjustas
Для тех кто не понял исходной проблемы — если бы аллокатор был достаточно быстрым, то не было бы нужны его переписывать.
0serg
А если бы в С++ была функция сделай_мне_хорошо() то на другие языки программирования никто бы и не смотрел. Ужасен язык где этой функции нет!
Мне как-то сложно вести беседу с человеком который не видит разницы между тезисами «штатный аллокатор — страшный тормоз» и «использование специфики задачи позволяет в некоторых ситуациях выжать дополнительные 20-30% производительности за счет использования специализированного аллокатора».
maaGames
Тут вы чуточку не правы. malloc/new требуют перехода из user-mode в kernel-mode и из-за этого производительность страдает. Насколько страдает зависит от количества миллионов аллокаций (в секунду). Для new специфические аллокаторы и память могут гораздо эффективнее использовать. Но против malloc не приёма, если нет другого malloc.)
0xd34df00d
Всегда ли? ЕМНИП реализации libc могут спокойно периодически запрашивать у ядра больше, чем надо, а потом отдавать память уже из этого пула.
maaGames
Чуточку же.)
Я вообще только про Windows выше написал, так как не знаю тонкостей работы менеджера памяти linux…
Перефразирую, чтобы не было разночтений:
В зависимости от реализации менеджера памяти на конкретной версии ОС получение объекта из пула может быть эффективнее получения памяти под объект из менеджера памяти.
Отсюда возвращаемся к лейтмотиву всей трилогии статей и всех комментариев к ним: не посмотрев в профайлер вообще говорить не о чем.
0xd34df00d
Вот с последним абзацем вообще в любом случае трудно не согласиться :)
0serg
Практически все менеджеры памяти работают в user-space.
Применительно к винде malloc работает на основе HeapAlloc, а последний выделяет под кучу память большим куском через VirtualAlloc (который правит page table и потому требует kernel space) и работает дальше в user-space, обращаясь к VirtualAlloc только для выделения больших блоков памяти.
Так что в винде malloc/new для блоков небольшого и среднего размера (<1 Mb примерно) как правило не требует перехода в kernel mode.
gandjustas
Вообще-то не вынес. За время каждого теста в C# происходит как минимум одна сборка мусора и она учитывается в общем времени забега.
0serg
Ну она же очевидно не _весь_ мусор убирает (плюсы — весь)? Почему добавление GC.collect резко делает ситуацию намного хуже? Вы бы хотя бы график «время в зависимости от числа итераций» построили чтобы было видно когда происходит GC и сколько он занимает. И запустили бы тест, для объективности, на одноядерной машине, потому как «вычисления в одном потоке, GC во втором» — это шулерство, реальный вычислительно нагруженный код займет все ядра.
Я бы предложил все же вписать GC.collect в конец теста, чтобы он вызывался _один_ раз а не сто, но это время было все-таки учтено.
Mrrl
А в какой задаче такое встречается? Может быть, и мне надо?
Казалось бы… многочлен — массив длиной (N+1)*(N+2)/2, индекс монома определяется по паре индексов переменных. Перемножение — все коэффициенты каждого на свободный член другого плюс двойной цикл по линейным коэффициентам. Если много многочленов от небольшого числа переменных, то придётся добавить пару таблиц мэппинга переменных. Где тут мелкие объекты?
0serg
Запись в виде плотной матрицы очень неэффективна, работа с несколькими матрицами 600x600, прямо скажем, не блистает быстродействием, а подавляющее большинство коэффициентов в этой матрице — нули. Эффективный код требует использовать одно из представлений разреженной матрицы и желательно — учитывать ее симметричность. Тут и возникают мелкие объекты переменной длины зависящей от степени заполнения матрицы.
Возникает эта конструкция в оптимизационной задаче на поиск оптимального расположения N поверхностей друг относительно друга. Параметризуем расположение поверхностей 6(N-1) переменными, записываем функцию ошибки для заданного расположения, строим для этой функции разложение Тэйлора 2-го порядка — получается квадратичная функция аппроксимирующая функцию ошибки, у которой минимум ищется решением линейной системы уравнений. Минимизируем квадратичное приближение — это не дает точного ответа, но дает приближение к нему, после чего повторяем тот же процесс разложения в ряд Тейлора уже в новой точке
Mrrl
Ясно. Я поступаю так: завожу матрицу 6N*(6N+1) (одну), потом для каждой пары поверхностей (у меня это облака) строю форму ошибки для их расположения (это плотная матрица 12*13), потом прибавляю её элементы к нужным элементам большой матрицы. Ну и потом — метод Гаусса. Использовать разреженность большой матрицы пока не пытался, у меня N не очень большие (максимум, 20-30).
encyclopedist
Разреженные матрицы обычно все-таки хранят в форматах не использующих «мелкие массивы переменной длины», вроде CSR.
Например в Eigen eigen.tuxfamily.org/dox-devel/group__TutorialSparse.html
или в scipy docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
0serg
CSR прекрасно попадает под определение мелкого массива переменной длины (один массив на матрицу)
Но здесь задача чуть проще, нет необходимости адресации произвольной строки, т.к. нет перемножения матриц (произведение любых двух квадратичных форм дает в выбранных ограничениях ноль). Зато и массивов два (линейная и квадратичная части)
Mrrl
И в терминах «ряда Тейлора» я над этой задачей не думал. Поступаю просто — записываю линейную часть сдвига точки в одном облаке, сдвига точки в другом, вычитаю одно из другого, прибавляю величину ошибки. Полученный линейный многочлен возвожу в квадрат, и все их складываю. Это для алгоритма «iterative closest point». Если бы подтягивал к касательной плоскости, поступал бы аналогично. То, что у сдвига сразу есть компоненты второго порядка, в голову как-то не приходило. Они действительно помогают?
0serg
Насчет помогают ли — вопрос спорный, т.к. сразу сделали с компонентами второго порядка.
Относительно старого подхода где поверхности позиционировались итеративно путем постепенного добавления +1 поверхности к уже поставленным N поверхностям он работает лучше :). А вот дают ли что-то компоненты второго порядка — не очень ясно. Теоретически могут, на практике толком не проверяли
Mrrl
А если сравнивать с подтягиванием всех поверхностей к текущему усреднённому положению? Понятно, что итераций будет больше, зато матрицы гораздо меньше.
0serg
Возможных вариантов много, мы не все пробовали, а текущий выглядит разумно эффективным :)
Mrrl
И просто из любопытства — каждая из поверхностей снимается неподвижным сканером, или сканер во время сканирования куда-то едет?
0serg
Сканер во время сканирования едет непредсказуемым образом (его держит в руках пользователь), мы это детектируем и компенсируем :)
Mrrl
Понятно. Он триангуляционный?
0serg
Нет, там достаточно малоизвестная технология, работает по принципу контрастного автофокуса. Я напишу потом статью о нем, интересная штука
Mrrl
Интересно. Нашёл парочку патентов, они используют специальные смещённые диафрагмы. Про вытаскивание глубины непосредственно из расфокусировки слышал лет 15 назад, но, по-моему, ничего из неё так и не сделали. Будет интересно почитать.
Сколько времени занимает реконструкция одного положения (без привязки)? И на каком процессоре?
0serg
Напрямую из расфокусировки тяжело что-то вытащить, но если добавить синхронизированную подсветку в плоскость фокусировки (по принципу конфокального микроскопа) и объектив с большой NA — то вполне реально. Дает очень высокую точность (20 микрон) и работает с полупрозрачными и бликующими объектами (мы сканируем зубы, так что для нас это важно, т.к. позволяет не покрывать зубы специальным порошком для сканирования).
Работает в реалтайме на не-топовом i7, латэнси первоначального (вполне приемлемого для визуализации) приближения <200 мс, а затем уже в менее жестких рамках нескольких секунд это приближении становится все точнее и лучше. Просто ведешь сканером и «открываешь» на экране поверхность.
dom1n1k
Жду третью статью, C# vs Java :)
gandjustas
А что мешает вам такую стать. написать?
tangro
Все эти «сравнения C++ и C#» заканчиваются в одном и том же месте: пойдите и предложите разработчикам драйверов, или ядер ОС, или движков баз данных, или браузеров, или ААА-игр, или протоколов, или видеокодеков использовать C#, ну потому что «почти так же по скорости, а писать удобнее». Дальше долго выслушивайте насмешки.
withkittens
Я лично вижу уже второй пост, который меряет время двух каких-то непонятных числодробилок, которые фактически вырождаются в замер скорости аллокации/сбора мусора, скорости работы памяти и работы оптимизирующих компиляторов. (И это помимо каких-то странных ограничений: «код на обоих языках максимально похожий» или «так пишут 9 из 10 программистов» (у автора явно накопилась большая статистика). На просьбы улучшить хотя бы то, что имеется — «ну форкай и напиши».)
А где кто-то пытался с шарпом залезть в ядро и драйверы — не вижу.
gandjustas
Так что улучшить то?
withkittens
1, 2, 3 (таки 9 из 10 программистов не знают про
std::swap
?), 4.gandjustas
Специально для вас сделал.
.NET Native работает примерно с той же скоростью, что и C++.
gandjustas
На одного программиста баз данных и драйверов приходится от 100 до 1000 (навскидку) программистов, которые пишут прикладные программы.
Если вы пишите драйверы, то у вас и вариантов мало, кроме C.
tangro
Смотря на статистику распространения C# и С++ по всяким разным рейтингам мы увидим одни и те же числа, ну, плюс/минус 30%, но уж никак не 1000х.
lair
geteventstore.com
ravendb.net
tangro
Ух, прям мейнстрим!
lair
Мейнстрим — понятие относительное, сегодня там одно, а завтра — совсем другое.
А если серьезно, то все понимают, что есть области, где недостаточно «почти так же по скорости», вне зависимости от удобства написания. Но с уменьшением «почти» этих областей становится меньше, и то, что раньше на «удобных» языках писать было совершенно непредставимо, сейчас пишется все чаще и чаще.
tangro
Мест, где небольшой вроде бы прирост производительности может существенно сэкономить байты\секунды\доллары тоже становится больше и кое-что наоборот с «почти такого же по скорости» переписывается на чём-то уровнем пониже, пример — habrahabr.ru/post/261205
lair
Это, на самом деле, очень интересный вопрос — становится ли таких мест больше в процентном отношении, или же нет.
RPG
В некоторых случаях в такие проекты не только шарперов на пушечный выстрел не подпускают, но и плюсовиков.
0xd34df00d
Предпочту держаться подальше от проектов и тимлидов с такой мотивацией для неподпускания плюсовиков.
bigfatbrowncat
Unity видели? На нем довольно солидные игрушки делают.
0serg
Выше уже приводили и пример и банальное контрзамечание что сам Unity на C++ написан
Chaos_Optima
Да неплохой двиг, а есть пример неплохого движка не на С++?
xaoc80
Когда-то я писал медиасервер и в тестах надо было вытянуть + одно full HD качество
Оставим за скобками вопрос можно ли подобные вещи писать на C# (что бы при этом на 3-х ОС минимум работало)
Но конкретно в этом проекте функции копирования памяти при преобразовании цветовых пространств удалось ускорить в разы
Я развернул циклы, сделал так что бы эффективное использовался кэш
А мой коллега добавил AVX инструкции там где это было необходимо
Результат — мы это сделали
Это банальный пример, но от этого он не становится менее «реальным» и «статистически» пригодным для Ваших рассужденй
fareloz
В целом — не вижу никакой пользы от этих двух статей. Нет универсального языка, который бы покрывал любой вектор целевых задач с любым множеством критериев оптимальности. Поэтому вравнивать несколько языков в виде статьи бесмыссленно — много критериев и нюансов. А говорить что один принципиально лучше другого — вообще абсурд. Адекватный специалист понимает, что язык — это инструмент.
В частности — оценивание языка критерием скорости работы априори субъективно — невозможно покрыть все возможные задачи и сравнить результаты, так как: критерии оценки разных потребителей сильно разнятся, некоторые задачи невозможно или нецелеобразно реализовать на одном из двух сравниваемых языков, да реализовать можно по-разному. В итоге выхлоп статьи нулевой и ее единственный вывод — что так делать не нужно.
OlegMax
Любопытно было бы заменить в этой задаче массив интов на массив структур и сортировать по одному из полей. В C++ влияние вполне предсказуемое, а вот не будет ли это катастрофой в C#? (просто предположение — я в C# вообще никак)
lair
При правильной реализации — нет, не будет.
X_OSL
Хотелось бы поблагодарить автора за статью и особенно пример на .NET Native, а также задать несколько вопросов:
1. Удалось ли понять за счет чего .NET Native несколько медленнее С++, не получалось ли сравнить disassembly?
2. Есть ли возможность использовать .NET Native с .Net Framework 4.0 (чтобы «разогнать» энтерпрайс, которому необходима поддержка XP:( )
3. Какие видятся причины для того чтобы не переходить на .NET Native с обычного managed С#?
X_OSL
В качестве ответа на 2 и 3 самому себе:
Похоже обычные десктопные приложения нельзя построить с .NET Native, а построить можно только приложения для Windows Store. Так ли это?
fareloz
Не знаток в такого рода деталях, но вроде даже в блоге Microsoft упоминалось, что данный момент так. Однако, вроде бы можно вручную с помощью какого-то инструмента скомпилиравать в Native (что не тоже самое, как минимум технология другая). Обычно это происходит автоматически для критических участков кода — система сама распознает. Но есть программы, вов ремя установки которых, установщик вызывает этот инструмент чтобы сразу все свои библиотеки скомпилировать под платформу.