Как стать автором
Обновить

Нужна система с низкими задержками? Выбираем Java вместо C++

Время на прочтение7 мин
Количество просмотров17K
Всего голосов 51: ↑23 и ↓28+6
Комментарии181

Комментарии 181

Если случайно сделать такое в C++, то можно получить

std::out_of_range

можно днями отлаживать ваш код на C++, пока в нем не останется ровно ничего лишнего

Profile-Guided Optimization есть и для C++

У плюсов есть серьёзный недостаток в виде чудовищной многословности (Java этим тоже страдает так-то) и просто совершенно дебильным рантаймом, который от выпуска к выпуску становится только фантасмагоричнее.

А что не так у плюсов с рантаймом?

Как недавно пролетала шутка с std::chrono::time_point<...> vs clock_gettime()

Вообще C++ тащит много хлама (генераторы числовых распределений?) в рантайм, но до сих пор не принесли чего-то приличного для асинхронного программирования. Корунтины в нынешнем виде пока не вдохновляют.

std::unordered_map в котором нельзя проверить строковый ключ, не создав строку

Зоопарк из [std::string_view;std::string;const char *]x[lvalue, const lvalue, rvalue], который создает минное поле. Но при этом нет строкового пула "из коробки"

Есть std::function и std::bind, но нет функции, создающей std::function из указателя на нестатическую функцию класса

Ну, это все-таки не совсем рантайм.

Есть std::function и std::bind, но нет функции, создающей std::function из указателя на нестатическую функцию класса

Функции нет, потому что этот вопрос глубже, чем кажется. Есть ограничения, завязанные на типы классов. В свое время и Борланд, и Майкрософт делали для этого проприетарные расширения в своих компиляторах.

Есть std::function и std::bind, но нет функции, создающей std::function из указателя на нестатическую функцию класса

Так std::bind и есть та функция что создает std::function из указателя на не статическую функцию класса. В каком смысле нет?

```

struct Foo { void f() {} };

int main() { Foo foo; std::function f = std::bind(&Foo::f, &foo); }

```

Вообще C++ тащит много хлама (генераторы числовых распределений?) в рантайм

Это вообще о чём? Про какой мир общаемся?

std::unordered_map в котором нельзя проверить строковый ключ, не создав строку

наверное нужно было посмотреть какие шаблонные параметры есть у этого класса и окажется, что возможность добавить такое вдруг появится. К тому же в новых стандартах ещё добавляют фич связанных с этим

Но при этом нет строкового пула "из коробки"

Опять не пойми из какого мира вообще это, что за стринг пулы? Откуда в списке вью, строка, указатель появились lvalue const lvalue и т.д.?

Есть std::function и std::bind, но нет функции, создающей std::function из указателя на нестатическую функцию класса

Опять же, в моём С++ почему то это есть и работает ровно как от них ожидаешь

https://godbolt.org/z/s88s681M6

НЛО прилетело и опубликовало эту надпись здесь

Как недавно пролетала шутка с std::chrono::time_point<...> vs clock_gettime()

Это же про типизацию. Да, второе короче, но первое позволяет отлавливать ошибки при компиляции.

Вообще C++ тащит много хлама (генераторы числовых распределений?) в рантайм

Не ясно, почему это хлам, довольно часто использующаяся возможность.

Но при этом нет строкового пула "из коробки"

А зачем он? Для ссылок на константы string_view хватает, константа будет в бинаре без какого-либо оверхеда. Для хитрых случаев с длинными строками можно написать элементарный контейнер, unordered_map из хэша в список shared_ptr на строки. Но очень специфично. Обычно совпадающие строки означают совпадение объектов, и это либо ключи в каком-то кэше, либо данные по другому ключу.

до сих пор не принесли чего-то приличного для асинхронного программирования

Предложения были, но их не приняли, т.к. решили развивать корутины. Тем не менее библиотеки на основе этих предложений есть, вполне удобные.

std::unordered_map в котором нельзя проверить строковый ключ, не создав строку

В c++20 уже можно.

Есть std::function и std::bind, но нет функции, создающей std::function из указателя на нестатическую функцию класса

Для этого используются лямбды. Хотите, захватываете this, хотите - делаете его параметром.

std::out_of_range
Вы всегда пишете
myvector.at(20)=100;
вместо
myvector[20]=100;?
И всем советуете работать с содержимым вектора через at()?
Если не хочу проверять диапазон и хочу исключение — да.
Иначе: используйте gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html.
Дорогой, медленный, ABI-несовместимый способ, зато без UB.
Предлагаете собирать OpenSSL всегда в debug mode, а то вдруг через 5 лет кто-то найдет там способ выйти за пределы массива?

Юзайте valgrind, или что-то подобное, и ваши волосы станут нежными и шелковистыми.

Мусорно-рекламная статья, повторяющая все те же доводы что и много лет назад. Ничего нового.

Ммм, то есть теперь разрабатывать на C++ стало намного легче, чем на Java, что ли?

Легче вообще на каком-нибудь luajit, пока у него трассирующий компилятор не завыделывается на слишком большом количестве трасс.

Статья использует аргумент о сложности С++ как доказательство того, что Java быстрее, чем С++. Я даже не знаю как будет выглядеть опроверждение такого доказательства. Наверное, С++ проще, потому что в нём три символа, а в Java - 4.

Не. На самом деле я не увидел в статье вот чего: в Java есть сборка мусора. А она далеко не всегда предсказуема. То есть, задержки в Java, в их самом неприятном виде, вызваны именно GC, который может отработать быстро, а может тормознуть — и никто не знает заведомо, когда как будет. С этим можно бороться, но это далеко не тривиально само по себе. Например, можно убрать GC — но вряд ли кому-то сильно понравится тот код, который будет получаться в итоге.

Я, честно говоря, ожидал в статье увидеть пример "запрещаем GC, работаем какое-то время, перезапускаемся" и его дальнейшее развитие.

EpsilonGC, да, так можно.

Я работаю над одной из таких систем. Мы избегаем аллокацию памяти любым путем.

Например String не используется почти нигде.

Код не похож на типичный Java, больше на ранний С++.

Я согласен с автором, производительность программистов намного выше если не нужно задумываться над UB в плюсах.

>производительность программистов намного выше если не нужно задумываться над UB в плюсах.
Ну т.е. она хуже, чем просто писать на Java, но лучше чем получается на плюсах?

Производительность может чуть хуже чем на обычной Java поначалу. Просто нужно привыкнуть к библиотекам.

Это примерно как на плюсах писать под embedded, где запрещен STL и все объекты созданы заранее.

После 12 лет на плюсах, у жабы моментальная компиляция и отличная интеграция со средой оч сильно ускоряют разработку.

А мне интересно, какой будет эффект в С++ от частого использования new/delete?

Скорее аргумент о безопасности, отсутствии UB и высокоуровневости, мне кажется.

Ну и правда, если они работают одинаково быстро, то зачем мучиться?

Это кто вам рассказывал про отсутствие UB в java?

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Best efforts означает, что если не получилось, то случилось UB.

Ой, ну эту проблему с итераторами в детском саду учат.

Конечно. В детском саду для программистов на Java. Программистов на (например) Rust этому не учат, потому что в Rust невозможно создать UB с помощью изменения объекта, по которому итерируешься.

Это к вопросу о том, есть в java UB или нет.

Только UB, которое Undefined Behavior, а не Unspecified, превращает ВСЮ программу в невалидную, со всеми вытекающими.

То что вы привели даже на Unspecified тянет с трудом.

О, кстати, у меня вопрос. А выполнение IO в java - это UB или не UB?

Я не понял вопроса.

В спецификации Java ничего похожего на UB C++ нет.

При этом добиться схожих эффектов технически можно, используя "секретные" техники, завязанные на конкретную имплементацию JVM, но обычным кодом этого добиться нельзя.

Описывает ли спецификация что произойдёт с программой во время IO? Является ли запись в файл defined behavior?

Во время какого именно IO? Оно принципиально отличается.

Но да, эффекты, получаемые в каждом конкретном IO описаны и специфицированны.

Одно из немногих исключений, когда спецификация слаба, и не указывает когда именно и какое исключение будет выброшено - работа с mmap (например, если другая программа обрезает замапленный файл). Но даже в этом случае это очень далеко от UB C++, потому как воздействия не полной спецификации поведения локализованы и не влияют на остальную программу и не крэшат JVM, например.

UB не должно ничего крашить, оно должно приводить к состоянию, когда поведение системы определяется деталями реализации, а не спецификацией. В том же C вы физически не можете крашнуть программу, если она запущена на процессоре без защиты памяти и без illegal opcodes. Т.е. UB в таком случае состоит в том, что компьютер "что-то делает", но никто толком заранее не может сказать что именно.

То, что вы говорите, называется Unspecified Behavior. И это нормально.

С++ критикуется не за него, а за Undefined Behavior, которое формально превращает программу в тыкву (и не обязательно, что очевидным образом).

Формальное описание тут: https://en.cppreference.com/w/cpp/language/ub

  • undefined behavior - there are no restrictions on the behavior of the program... and the compiled program is not required to do anything meaningful.

Теория конечно хорошо - но смысл в статье нулевой, т.к. нет примеров.
Покажите пример, на какой конфигурации, с какими флагами оптимизации выполнялся и кто и на сколько быстрее оказался.

Так вот же, всё в статье. FUD быстрее, Java побеждает С++ по скорости!

Очередная бредовая статья про мифический С++, в котором нужно управлять памятью(нет, не нужно), вручную перекладывать байты по указателям(нет, не нужно), якобы не проверяются простейшие вещи типа выхода за пределы массива(проверяются, при том только на дебаге, то есть 0 оверхеда), загоны про плохие IDE по плюсам это вообще что то невероятное по уровню бреда, видимо расчёт на тех кто ни разу не видел С++ и его IDE
Отдельного упоминания конечно требует вот этот фрагмент:

по моему опыту, Java (в большинстве окружений) просто лучше распознает, какие фрагменты кода выполнять не обязательно, а какие критичны для функционирования программы

То есть в плюсах ещё и компилятор плохой оказывается, такие "мои ощущения" нужно подтверждать статистикой, которой нет, потому что это полный бред

Просто нужно писать на Java как на C++, на каждом этапе разработки не забывая об управлении памятью.

Ахахаха, прям как коммунисты придумали себе злобный капитализм, научили ему в вузах, а потом сами этот выдуманный капитализм и построили на руинах империи

С первых абзацев статьи стало понятно что автор на C++ никогда не писал.

P. S.

прям как коммунисты придумали себе злобный капитализм, научили ему в вузах, а потом сами этот выдуманный капитализм и построили на руинах империи

Едва не умер от смеха, до того меткое описание.

НЛО прилетело и опубликовало эту надпись здесь
якобы не проверяются простейшие вещи типа выхода за пределы массива(проверяются, при том только на дебаге, <...>)

То есть не проверяются.

если ты входишь в 0% тех, кому это нужно на релизе, то ты используешь метод at() и проверяешь всегда

Оно вообще-то нужно всем. Вдобавок, если я буду писать at, это не сделает магическим образом проверяемой индексацию в зависимостях

Обсессивно-компульсивное расстройство это проблема, не нужно навязывать его всем во всех языках программирования

С интересом посмотрю на браузер на java. Как быстро он будет работать? Наверняка быстрее, чем хром и фф, да?

С интересом посмотрю на браузер на java.

Но статья про системы с низкими задержками, а не про браузеры или про заменить весь C++ на джаву.

Скажите, а по какому признаку вы определяете, что браузер "быстрый"? Лично я - по времени отклика на изменения (будь то input latency или время загрузки страницы).

Соответственно, если на java нельзя написать код, который быстро может нарисовать букву в поле ввода после нажатия кнопки на клавиатуре (с поправкой на javascript, css и т.д.), то и в других областях "низкая задержка" - это фикция.

Последний раз, когда я разбирался с вопросом низкой задержки в коде, меня интересовали куда-то непонятно девшиеся 2 ps. Я с трудом представляю себе приложение на java, которое бы знало, что такое 'ps'.

В данном случае речь идет о трейдинговом ПО, об этом сказано в начале статьи. И основные проблемы там связаны с gc.

Что характерно — в тексте статьи нет упоминания GC. Эта аббревиатура есть в комментариях — вашем и моем. Из чего лично я сделал вывод, что статья низкого уровня — не упомянуть GC в обсуждении задержек это ужасно.

Ну качество статьи это тоже несколько другой вопрос,я согласен что это хорошо было бы упомянуть, но то что она про определенный тип систем это сказано вначале.

В этой статье я рассмотрю в качестве примера конкретный класс программ, в которых высоко ценится низкая задержка: трейдинговые системы.

Поэтому мне кажется что замечение про браузеры тут не очень релевантно.

Так для трейдинга как раз GC большая проблема. А браузеры да, другой класс задач.

Да, я это и пытался сказать, верхний комментарий ветки про браузеры, а статья про трейдинговые системы.

Аббревиатуры - нет. А сборщик мусора - есть. Но кого это волнует, читать же необязательно

Вообще-то, я статью прочитал. В ней например упоминается Disruptor, как решение проблем GC. Я про него знаю с момента появления — и знаю, что решением этих проблем он не является. Это не универсальное решение совсем, никаким боком, устранить задержки GC в общем виде он не может — и не должен.

Он не специфичет только для трейдинга — но он ничем не поможет типичному «браузеру», о котором тут начали говорить. И всякие варианты использования off heap памяти, чтобы убрать GC — тоже не помогут, потому что если их начать применять, сложность и стоимость разработки сразу подскочит, надежность упадет, и станет все почти как в случае плюсов.

Что характерно — в тексте статьи нет упоминания GC. Эта аббревиатура есть в комментариях — вашем и моем. Из чего лично я сделал вывод, что статья низкого уровня — не упомянуть GC в обсуждении задержек это ужасно.

По комментарию видно, что человек не заметил сборщика мусора в статье, возможно не увидел. Ну, я помог, мне не лениво. GC есть, задержки упомянуты.

Так я без претензий. Да, я выразился неаккуратно, упомянут. Но если бы статью переводил я, будучи программистом, а не Полина Семенова, переводчик, там было бы везде GC, просто потому что статья техническая. И тем кто является ЦА, так понятнее.

А в чём разница между gc, который тормознул мой ввод буковки в поле ввода и тормознутой транзацией? Обе ситуации ужасны.

Я привожу в пример браузер, потому что трейдинговые автоматы есть не у всех, а увеличение latency в браузере могут себе представить даже не-программисты.

Я привожу в пример браузер, потому что трейдинговые автоматы есть не у всех

Так статья про трейдинговые системы, у них свои узкие места. И идея в статье что джава вполне подходит для них и не будет узким местом.

Ну, некоторая разница все же есть. Для трейдинга можно написать такой код (затратив много сил, на самом деле), где практически не будет GC. И поскольку JIT компилятор у JVM таки реально достаточно продвинутый, такой код будет быстрым. Зачастую быстрее, чем средний человек сможет написать на плюсах. А вот написать такой браузер — мне кажется нифига не окупится.

>latency в браузере
Во многих случаях проблемы с кнопочками — это проблемы UI, который как правило однопоточный, т.е. вычисления должны быть в другом потоке. И это очень часто пишут криво. Так что глядя на тормозящий браузер я бы скорее предположил бы это, а не GC (хотя конечно, и он может иметь место). И это не особенность только Java — на многих языках написать тормозной UI не проблема :)

Да откуда вообще на плюсах взяться среднему человеку.

"практически не будет GC" - это значит, что "иногда" решение будет приниматься раз в 1000 медленнее, чем обычно. Т.е. в принципе, оно как-то будет работать, но иногда конкуренты будут в 1000 раз быстрее.

И это мы говорим не про то, что можно не успеть сделать транзакцию, а про то, что можно не успеть отменить невыгодную транзакцию.

>«практически не будет GC» — это значит, что «иногда» решение будет приниматься раз в 1000 медленнее, чем обычно.
Ну, нет, в общем случае не значит. GC можно вырубить совсем — просто это будет достаточно дорого (в разработке в первую очередь). Тут уже отписались люди, которые это реально делают — например, нельзя использовать стандартные строки, т.е. писать в таком стиле может быть весьма непривычно.

Ну или скажем, можно применить NoGC — тогда если память вдруг исчерпается, приложение упадет. Но если ваши алгоритмы такие, что вы можете заранее оценить объем потребной памяти — то вполне себе вариант. Память нынче конечно подорожала, но поставить ее столько сколько нужно — уж для трейдинга не проблема.

на самом деле проблема, потому что обычная тормозная DDR4-память в трейдинге никому не интересна, имеет значение лишь размер L2/L3-кэша, в которые и помещается практически весь код и данные, обращения же ко внешней памяти минимизируются до предела. И если у вас уже весь доступный кэш процессора распределён — добавить его невозможно до выхода нового поколения CPU.

>И если у вас уже весь доступный кэш процессора распределён
Если вы уже оптимизируете на таком уровне — то скорее всего вы от Java уже отказались. Потому что уменьшить GC до нуля — это одно, и это в общем возможно (хотя и немалыми усилиями), а вот уменьшить потребление памяти, а тем более влезть в кеш — это совсем другая задача.

никто и никогда не рассматривал Java в качестве платформы для трейдинга, она была неприемлемо медленной даже когда задержки измерялись секундами.

Вот прямо тут где-то в комментах люди отписались, что это делают. Отключают GC, и делают много чего вручную. Для HF трейдинга — наверное нет (лично я и не обещал нигде), но это не весь трейдинг.

в чём разница между gc, который тормознул мой ввод буковки в поле ввода и тормознутой транзацией

Просто для контекста - в браузерах уже давно есть как минимум 1 gc - в js-движке. Более того, в Хромиуме для управления некоторой памятью в C++ тоже GC используется (интересно, кстати, было бы его сравнить с G1 или ZGC - jvm позволяет использовать намного более интересные GC по сравнению с C++). Я бы не связывал отсутствие браузеров на джаве только с GC.

А теперь представьте, что у вас есть gc, который подтирает за другим gc... О, боги.

GraalVM/Polyglot как-то справляется. Там, вроде бы, один GC и JIT на все рантаймы - js, java, ruby. Я бы сказал, что это даже более красивое решение, чем то, что в Хромиуме сделали.

НЛО прилетело и опубликовало эту надпись здесь
весь C++ на джаву.

Хорошо, если на джаву, а не на джаваскрипт, как сейчас стало модно.

если бы эти проекты стартовали сегодня с нуля то у c++ браузера не было бы шансов. как сегодня нет c++ ide. если всё сделать идеально, то конечно с++ будет лучше, но вы не сделаете за сравнимый бюджет. я вот 16 гигов не для хрома с фф покупал, но смирился, так же бы и java было

вон в ml и биг дате вообще питон используется, хотя задачи полностью вычислительные, я щитаю, что это безобразие.

как сегодня нет c++ ide

Visual Studio (которая не Code, а полноценная), насколько я знаю, на C++ написана.
А вот продукты JetBrains — уже на Java.


вон в ml и биг дате вообще питон используется, хотя задачи полностью вычислительные

Там C++ бэкэнд.

Visual Studio (которая не Code, а полноценная), насколько я знаю, на C++ написана.

Вот, кстати, отличный пример. VS действительно была написана на C++, но MS есть MS, там различные департаменты борются между собой, и было решено переписать VS на .NET. Выход оболочки долго задерживался, программисты латали проблемы с перформансом. И вот, наконец, VS вышла на дотнете, и сразу же затормозила. Проблемы с производительностью не прекращались до самого выхода превью VS 2022, которое уже 64 битное. А ведь это всего лишь UI.

как сегодня нет c++ ide

QtCreator же

В ml и bigdata снизу сишечка во все поля, плюс нет никакой потребности в low latency.

Все куда проще.

На Си и тем более на плюсах можно понять уровень абстрагирования, в том числе и управления памятью, до того же как джаве и выше. А можно наоборот, опуститься до уровня машинных кодов и железа. И типичное решение, когда весь вспомогательный код не особо оптимален, зато легкоотлаживаем и быстро написан. А скоростной код хоть и сложен в отладке-написании, но его мало.

Джава не дает такой широты уровней. И единственный плюс этого, что для овладения им тоже такой широты не требуется.

Поскольку C++ очень близок к металлу

К тяжелому?

bare-metal

НЛО прилетело и опубликовало эту надпись здесь

Конечно. А вы шутку не поняли? В оригинале вместо "bare metal" было просто "metal", которое правильно переводить на русский как "железо", но никак не как "металл".

Это когда из одежды только гитары и барабанная установка?

Такое чувство, что сейчас на дворе начало 2000х, и людей нужно убеждать, что джава быстрый язык. Мне казалось, что последние лет 10 уже никто не сомневается, что джава быстрая.

Загадка от Жака Фреско:


Что важнее — передать управление функции которая примет решение покупать ли актив на сумму в несколько сот тысяч долларов или начать освобождать память от объектов на которые не осталось доступных ссылок?


На размышление даётся 740 миллисекунд.

  1. Что-то многовато времени

  2. Не защищая Java - NoGC можно и там, но выйдет не проще плюсов

  3. Нужно понимать, что не latency единой жив подобный «алго-трейдинг», важен еще и throughput, о чем фанаты решений на Java не очень любят вспоминать

с throughput всё тоже отлично, проиграете вы в худшем случаи 20% от голой c++ программы при этом в ваших руках будет совершенно другой по силе и удобству функционал. 20% я взял от бенчмарка рейтрейсера, и это с округлением не в пользу java.

"NoGC можно и там, но выйдет не проще плюсов"

а можно просто памяти дать больше и процессор хотя бы на 4 ядра поставить, и вполне без stop the world жить, это ж никакое не обязательное свойство работы java.

Если вы выбрали первое, то введите код из смс в тоновом режиме.

Просто надо делать пулы часто создаваемых и удаляемых объектов, тогда скорость "горячего" пути будет намного выше.

Можно.
Но что-то подсказывает что выбирать столь высокоуровневый язык и менять архитектуру программы для учета того как внутри работает GC — не лучшая идея.

Не соглашусь: учитывать детали реализации вашего инструмента важно, чтобы пользоваться им эффективно. Иногда эффективность не нужна в принципе, тогда да, не важно, как оно там внутри.

Это как раз путь получить долгие паузы на GC. Скорость GC пропорциональна количеству живых объектов, повторяю - живых объектов. Если я выделил миллиард объектов, но они стали недостижимы через 3 сек, то и выделение и очистка этой памяти считай были бесплатными. А вот когда вы в пулах начнёте держать миллионы изменяемых объектов, вот тогда вы начнёте огребать паузы в GC.

Миллионы обычно не нужны. Десятков, в крайнем случае сотен, обычно хватает.

А почему бы тогда просто не выделить эту сотню объектов по мере необходимости и не избавить себя от управления жизненным циклом? Я только одну причину вижу - объекты очень дорого инициализировать.

Накапливается "мусор", который потом пытается удалять GC.

Я об этом писал выше - GC плевать сколько у вас мусора накопилось, он не удаляет никакой мусор. Имеет значение количество живых объектов. GC найдёт живые объекты и переместит их в начало сегмента памяти, в процессе перетрет мусор данными живых объектов. Скорость GC зависит от того сколько ему нужно живых объектов проверить и скопировать. Количество мусора (недостижимых объектов) вообще не имеет значения, кроме крайних случаев.

Ладно, расскажу случай из практики.

Делали VPN под Android, ам поднимается TUN-интерфейс, и из него или в него читаются/пишутся пакеты. При большой скорости Wi-Fi и скачивании больших файлов была очень маленькая скорость через самописный TCP/IP-стэк. Конечно, в самом начале подсчёт чек-сумм пакетов был вынесен в либу через JNI, это понятно. Но тормозило. После добавления пула пакетов всё ускорилось в несколько раз.

>Конечно, в самом начале подсчёт чек-сумм пакетов был вынесен в либу через JNI, это понятно. Но тормозило.

Посчет чексумм лучше делать в Java. При использовании JNI вы платите за а) маршалинг аргументов б) пининг буферов. Второе в вашем случае важно, потому что это означает, что GC не может двигать эту область памяти пока выполняется JNI метод. Если предположить, что подсчет чексуммы это существенная часть работы, то вот вам и ответ на вопрос почему тормозит - память сильно фрагментируется из-за пининга, GC выделяет новые регионы памяти, потому что в старых куча дырок, но двигать данные внутри региона нельзя. Память начинает заканчиваться - GC запускается все чаще.

Как только в этой картине появился пул, исчезла проблема фрагментации памяти.

Но я повторюсь - это не проблема того, что приложение генерирует мусор. Это проблема того, что у вас слишком много живых объектов.

Поддержу и добавлю: делать выводы без профилирования странно. А профилирование наверняка покажет схожие в вашими выводами результаты.

Напомню, что Android это не Java. Оно не соответствует спецификации, обладает принципиально другими ограничениями и сборщик мусора тоже отличается.

Есть такая метрика, memory footprint. Если у приложения много страниц в working set, но в каждой страничке используется по 20 байт всего, использование памяти становится неэффективным, т.к. данные начинают вымываться из кешей, а то и вообще целые страницы в swap.

«Взрослые дяди» работают с No GC.

Ну вообще-то цель пула — убрать создания и удаления объектов, или сократить их количество. Т.е. пул один раз аллоцируется, и остается живым. И это один объект, а не тысячи (массив, например), если все сделать правильно. Изменяемых — да, скорее всего плохо будет.

“Удаления” объектов в Java считайте что нет, забываем про финализаторы. Создание объекта это бесплатная операция, увеличение счётчика. Пул имеет смысл только если конструктор или инициализация объекта какая-то очень сложная. А как решение проблем с GC это так себе идея.

>А как решение проблем с GC это так себе идея.
Дело в том, что это не просто решение проблем GC, это широко используемое решение таких проблем. Правда, оно не для любых объектов годится, но когда годится — оно отлично работает.

Верно, оно годится для двух случаев а) буферы из байтов б) десятки или сотни объектов, которые долго создавать. На этом все.

Вот, например, посмотрите тут неплохая серия экспериментов.

According to the GC pauses data, the pooling strategy is actually the worst one when it comes to the real-time operation. It simply does not work – nothing that shows pauses of nearly one second can be considered real-time.

В статье используется пул буферов - массивы байтов это лучший кандидат для пула. Вот еще хорошая цитата

The garbage collector is indeed the biggest risk factor. Pooling introduces very many live objects that causes infrequent, but long GC delays. Allocation, however, overloads GC completely. Besides, at the times of high load (our case C) there may be many live objects anyway, and there allocation fails miserably. So pooling is still better.

Жирным выделил ровно то, о чем я говорю. Пулы тащат для решения проблем пауз GC, но пулы могут ухудшать ситуацию с паузами, делая их еще длинее.

Проблема с GC вылезает в моменты пиковой нагрузки, когда а) система перегружена и тормозит сама по себе б) объектов выделяется много в силу большого числа запросов в) объекты живут долго, ибо система тормозит. Вот тут наступает полная опа. Одно из решений это не допускать таких режимов работы. Другое - положить множество живых объектов в пул, система будет работать лучше чем без пула, но все равно будут длительные паузы.

>Верно, оно годится для двух случаев а) буферы из байтов
Ну да, примерно. Я бы сказал, что сами объекты должны быть фиксированного размера, т.е. хешмапы, и даже строки так не получится хранить. Но это таки достаточно широкий класс задач, хотя бы потому, что буфер из байтов — это просто аналог памяти, и на нем можно сделать свой аллокатор, хотя бы в теории. Я согласен что тут возможна полная жопа, хотя бы потому, что трудоемкость таких решений сильно выше, чем решений на идиоматической Java с GC.
Можно придумать какие-то компромисы, например не делать пул сразу на миллион объектов, а разбить его на несколько арен по 100 объектов. Кончается одна сотня — выделяется следующая. При выделении объекта приоритет отдавать минимальной арене, а пустые арены (кроме первой) удалять.

В идеале, библиотека пулов должна выделать память не в управляемой куче. Тогда JVM не будет знать, что выделенная память, но не отданная приложению из пула, является живым объектом, и не будет обходить его при GC. Не знаю, есть ли такое решения. Они явно unsafe и заточены под конкретную JVM.
“Удаления” объектов в Java считайте что нет, забываем про финализаторы. Создание объекта это бесплатная операция, увеличение счётчика. Пул имеет смысл только если конструктор или инициализация объекта какая-то очень сложная
Нет, потому что если новый объект сконструирован по новому адресу, процессору придётся подгрузить новый адрес в кеши, а какую-то старую кеш-линию вытеснить. Если же объекты крутятся по одинаковым адресам, траффик с памятью сильно падает.

Паузы в некоторых современных GC пропорциональны не числу живых объектов, а числу Root'ов: "безусловно" живых объектов, с которых начинается сканирование.

Объясните, почему?

Допустим, у меня один root — это HashMap, в котором лежит 100.000 связных списков, в каждом из которых 100.000 элементов.

И другая ситуация — один root, в котором лежит класс Student из учебного примера с двумя полями типа String.

Число рутов одинаковое, время работы GC тоже будет одинаковым?

ZGZ или Shenandoah GC и Mark, и Compact делают параллельно, не блокируя работу приложения. Т.е. время работы конечно разное, но на latency скажется именно скан root'ов, который таки происходит в stop-the-world паузе.

на latency скажется именно скан root'ов
Что выполняется в этой операции? Рекурсивный обход всех объектов, доступных из этого root, или что-то другое?

Собственно, собирается их список.

Список только рутов (собственно, зачем?) или список всех объектов, доступных из этих рутов (но тогда прав ваш оппонент, утверждающий, что важно не количество рутов, а количество живых объектов).

Именно список рутов. Не понимаю вопроса "зачем". Напомню, что в него входят и те объекты, которые доступны в текущем потоке (очень грубо говоря - лежат на стэке).

Получили список только рутов. А дальше что с ним делает GC?

В моём понимании, GC должен получить полный список живых объектов, чтобы всё, что в него в не входит, добавить в свободную память или очередь на финализацию.
НЛО прилетело и опубликовало эту надпись здесь
Но нужно как-то идентифицировать объекты, созданные после запуска обхода, чтобы их потом не пометить, как недостижимые. Первое, что приходит на ум — в заголовке объекта хранить его дату создания (ID из последовательно увеличиваемого генератора), и фиксировать текущий ID при старте обхода.

Или есть какие-то более элегантные решения?

Если не стоит задача собрать всё недостижимое за один проход, то можно различать не по ID объекта, а по региону, в котором он лежит. В этом случае регионы, куда писали после запуска обхода, помечаются как исключённые из текущего обхода.
Это позволит хранить меньше служебных данных (можно не давать объекту уникальный ID), но я, правда, не уверен, что это так уж сильно "элегантнее" — потому что система должна будет при этом знать, какие регионы сейчас обрабатываются GC (в них не следует писать в это время), и с пометкой регионов будет потенциальная гонка состояний.

Не понимаю, как можно исключить регион из обхода.
Какой-то объект из региона A ссылается на объект из B. Если A исключим, мы часть объектов из B не увидим.

Да и то решение, которое я выше предложил, дырявое…
Например, есть цепочка ссылок
A → B → C → D.
Допустим, gc дошёл до объекта C и тут другой поток обнулил в C ссылку на D, а поставил в B ссылку на D. И всё, объект D не будет помечен как живой. Тут не зря 0xd34df00d написал что такое может работать только для иммутабельных объектов. К java это не применимо.

Я неправильно выразился, вероятно. Вернее было сказать "всё, что находится в регионе, куда недавно что-то писали, помечается как достижимое". То есть, внутри региона не то чтобы совсем проверок нет, но они все упрощённые, нам нужно сравнить адрес с краями "безопасных" регионов, и всю иерархию от этого объекта и вниз мы сразу можем пометить как strong reachable. Но у этого подхода тоже есть свои дырки — при высокой фрагментации по регионам, определённый код может в такой системе держать все регионы памяти "безусловно достижимыми", и рано или поздно память из-за этого кончится. Решается, полагаю, комбинацией этого способа с эвристикой и применением более злой очистки если обнаружено, что память уже прямо совсем нужна, но ничего давно не чистили.


К java это не применимо.

Да, Java мутабельна. Тем не менее, каким-то образом это в Java работает на достаточно хорошем уровне, чтобы такая перестановка ссылок (а описано банальное удаление элемента из LinkedList) не приводило к пометке живого объекта неживым. Мне не хватает знаний о деталях реализации, чтобы точно ответить, почему этого не происходит. Может быть, у недавно присвоенных объектов есть подобие иммунитета. Хотя это требовало бы значительного расхода памяти, так что вряд ли настолько наивно сделано.

всё, что находится в регионе, куда недавно что-то писали, помечается как достижимое
Очень сомнительно. Это на каждую запись в память нужно сгенерировать на порядок больше кода, который определит, к какому региону относится адрес, и сделает пометки в таблице регионов.

Всё применимо, именно поэтому окончательное решение происходит в короткой stop-the-world паузе (а она наступает кооперативно через достижения так называемых safe points). Именно там вы понимаете, что на объект появились ссылки (все такие объекты помечаются как достижимые для этой фазы, если очень-очень упрощать).

Это какая-то магия… Мы долго обходили-обходили большой граф объектов, при этом другой поток параллельно менял ссылки и в часть объектов мы не попали. Потом наступает короткая пауза и мы откуда-то узнаём, в какие объекты мы не попали…

К счастью, сборка мусора в Java достаточно кооперативная.

В завершение первой начальной короткой паузы все потоки считывают факт наличия сборки мусора и сохраняют его в регистр. В коде при ЗАПИСИ ссылки в поле объекта (т.е. в т.ч. при обновлении) проверяется регистр, и если сборка мусора активна, то объект, на который раньше ссылалось это поле помечается живым безусловным образом. По крайней мере в Shenandoah GC.

Пометка активным делается в тредлокальном буфере. Потом в короткой паузе все тредлокальные буфера собираются и эти объекты считаются живыми. При переполнении буфера он флашится в общий буфер (что дороже, чем тредлокальный по очевидной причине).

Если я правильно помню, то ZGC пометки делает на чтении, а не на записи.

Спасибо. Такой вариант представляется мне рабочим, если закрыть глаза на оверхед при каждой записи (в альтенативном случае — чтения) ссылки.

Только этот объект нужно не просто помечать живым, а добавлять в корни обходов, т.к. живыми также должны стать объекты, достижимые из этого.

Не совсем корень обхода, но да, подразумевается, что из него тоже нужно проходить, отслеживая достижимость.

Оверхед на такой барьер (отслеживание записей) в самых грустных бенчмарках около 2х процентов, в средних ближе к 0.

Ещё раз рекомендую посмотреть доклады Шипилёва, гуглится по сочетанию Shenandoah Шипилёв, потому как проблема, о которой вы беспокоитесь - наиболее простая из спектра сложностей, и решена была ещё в древнем CMS (который настолько древний, что его уже даже выпилили).

В зависимости от сборщика мусора, это может работать по-разному, но общий принцип обычно связан действительно с регионом памяти.

До запуска фазы маркировки объектов, вы помечаете, какие регионы будут дальше использваться для аллокации объектов. Весь этот регион не трогается на данной фазе сборки.

Это в предположении, что объекты неизменяемые. В java это не так.

Можете посмотреть на shenandoah, Алексей Шипилёв подробно рассказывает, как оно работает.

Неболшой оверхед очевидно есть, но там вполне прилично.

Нашёл только слайды, по ним непонятно.

Маркируются живые объекты.

Потом живые объекты переносятся в другой регион памяти. В самых современных сборщиках мусора перенос объектов происходит без блокировки (достаточно хитрыми алгоритмами).

Всё что не было перенесено считается мёртвым, а память свободная. Объекты с finalize или на Weak/Soft/PhantomReference трэкаются чуть сложнее.

Это алгоритм стандартного сборщика, где маркировка происходит при полной остановке (иначе часть объектов не достигнем, а значит посчитаем мёртвыми).

На размышление даётся 740 миллисекунд

ZGC заявляет не более 10мс на 99.99 перснициле, так что 740мс это небольшое преувеличение.

а в скором времени и 1мс

Если ваш таймфрейм 740 мс то активов вы покупаете на тысячи долларов. А ещё просто не держите много объектов долго в памяти и тогда ее выделение и освобождение будут вам почти бесплатно. Все эти проблемы с GC очень сильно преувеличены.

Функция которая примет решение покупать ли актив, с вероятностью ровно 50% определит, будет ли этот актив в будущем дорожать или дешеветь - либо угадает, либо нет. В отсутствии брокерской комиссии матожидание от сделки равно нулю. При брокерской комиссии равной х, матожидание сделки равно -x. Очевидно, что ни в коем случае нельзя передавать управление функции, которая торгует с отрицательным мат.ожиданием, следовательно надо передать управление гарбадж коллектору. Привожу псевдокод идеального трейдинг-бота:

while(true) {
  if (false) then
    bestTradingFunction();
  else
    runGarbageCollector();
}

Функция, которая покупает или продает актив на таймфрейме в 740 мс ничего не предсказывает. Они делает это либо потому что обязана в силу договора о маркет мейкинге, либо потому что есть арбитраж. Брокерские комиссии, в случае маркет мейкинга, скорее всего отрицательные (вам платят rebate), либо вам дают retail ордера с которыми торговать всегда выгодно.

По вероятности события еще интересно. Если такое происходит раз в сутки и длительность 100 мс, для большинства применений это не актуально. Скорее всего вероятность определяется типом кода, если там используются String в которых гигабайты данных, память придется чистить часто, если объекты создаются только при старте и далее практически не меняют расположение в памяти, очистка памяти может годами не потребоваться.

Java написана на c++. Т.е. проект на java - частный случай проекта на c++.

А С++ частный случай ассемблера? )

А сам ассемблер - ненужная абстракция над бинарными кодами?
Получается, что лучший язык программирования - азбука Морзе.
0110100101011100101001001001010010110100101000100101 ой, т.е.
.-...-..-.-.--..-.-.--.---.-.-.---..--.-.--....--...... ))))

Ну и в целом по топику, jvm/jdk их же много, на плюсах - старинная sun, а уже что-то посовременнее, типа граальвм - она вроде вся на джаве.

Про программирование в двоичном коде есть замечательная статья

https://habr.com/ru/post/152052/

Не влезая в сам холивар, хочется спросить у автора перевода, а вам материал хоть кто-нибудь вычитывал перед началом перевода или, хотя бы, публикацией?

Статья свежая, вопросов нет, но...

Например, есть LMAX Disruptor, трейдинговая платформа с низкой задержкой, написанная на Java

Во-первых, LMAX действительно трейдинговая система, а вот LMAX Disruptor - лишь реализация lock-free кольцевого буфера. Которых полно, наверное, на любых языках программирования.

Далее, переходим на сайт https://lmax-exchange.github.io/disruptor/disruptor.html где как раз описаны тесты производительности и видим...видим сравнение на следующей конфигурации

The following table shows the performance results in operations per second using a Java 1.6.0_25 64-bit Sun JVM, Windows 7, Intel Core i7 860 @ 2.8 GHz without HT and Intel Core i7-2720QM, Ubuntu 11.04

...

Linux 2.6.38 64-bit

...

Mean latency per hop for the Disruptor comes out at 52 nanoseconds compared to 32,757 nanoseconds for ArrayBlockingQueue

Java 1.6 / Java 6 была выпущена в ноябре 2006

Intel Core i7 860 - Q3 2009

Intel Core i7-2720QM - Q1 2011

Ubuntu 11.04 - 28 апреля 2011 года

Только у меня складывается впечатление, что вся статья родом из тех же времён, где-то из мая-июля 2011 (Java7 была выпущена 28 июля 2011 года), когда разработчикам из LMAX пришлось изобретать велосипед с lock-free алгоритмом? И всё, что написано уже не актуально не только по отношению к С++ но и к самой Java?

Disruptor да, продукт где-то 10-летней давности минимум. И все что тут про него написано, уже давно известно всем, кому это может быть интересно.

Да вот хоть этот пост возьмите — это 2011 год. По-моему это первый.

>И всё, что написано уже не актуально не только по отношению к С++ но и к самой Java?
В значительной степени, скорее всего.

Чем больше роботов будет написано на яве, тем больше можно будет заработать на арбитраже.

Ну узнали вы на 1мс раньше, что появилась новая ценовая заявка. И что? Где арбитраж? Вы увидели, что ценовая заявка на 1% выше существующей цены. "Ага, цена пошла вверх, надо покупать" А хрен там. После этой заявки появятся другие, которые обрушат цену аж на 10% вниз. И всё - та же самая лотерея, никакого арбитража.

НЛО прилетело и опубликовало эту надпись здесь

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

В то, что тех. анализ не работает в этом уже почти никто не сомневается, но многие ещё наивно верят, что если запустить тех. анализ на 10 нс раньше соседа, то тогда анализ сработает. Типа ну я же раньше получил эту информацию, значит могу ее использовать в целях обогащения. Только это не информация, а белый шум.

НЛО прилетело и опубликовало эту надпись здесь

Практика показывает, что один цикл до постановки заявки это 12-16 ms, из которых примерно 12-16 ms уходит на получение фида по сети. Что за этом фоне единицы наносекунд? Если вы в рамках этого цикла выигрывали сотню наносекунд, то прям терзает смутное сомнение, что это оказывало какое-то заметное влияние.

НЛО прилетело и опубликовало эту надпись здесь

Ну так в горячих местах все сражаются за единицы наносекунд, и в Java тоже. 1 мс - это (очень) условно худший случай в тех редких случаях, когда GC таки делает stop-the-world паузу.

погуглите что такое арбитраж, но есть мнение, что на нём вам биржа заработать не даст, она сама с удовольствием это сделает, ну или продаст вам за дополнительную плату тёплое место с маленькими задержками. но опять, из того что я краем уха слышал от бизнесов, которые этим занимаются, там доходность на уровне 7% годовых, а в некоторые месяцы и 0.

Я бы сказал, что идея статьи в целом мне близка, но вот предметная область выбрана не очень удачная.

Кроме того, как минимум, по моему опыту, Java (в большинстве окружений) просто лучше распознает, какие фрагменты кода выполнять не обязательно, а какие критичны для функционирования программы.

Не понял, а что значит "какие фрагменты кода выполнять не обязательно"? Как это?

НЛО прилетело и опубликовало эту надпись здесь

Я тоже подумал, но это странно. Этот код не "не обезательно" выполняется, он вообще не выполняется

НЛО прилетело и опубликовало эту надпись здесь
Автор скорее всего имел в виду JIT. Именно потому, что «вы не знаете до запуска, какая» — а он работает после запуска, и может кое-что о коде узнать.

Вообще по описанию похоже, будто автоматически определяется, что возвращаемые значение функции (или часть из них) были проигнорированны, и java сама может выпилить вычисление этих значений, без побочных эффектов. Звучит круто - это какая-то серьезная эвристика. Неужели в джаве такое есть? Было бы интересно

Возможно не совсем по теме, но вот пример довольно интерересной оптимизации: https://habr.com/ru/post/305894/

Автор пишет про выход за границы массива... Автор не слышал про range-based loops?

ну кроме того, что доводы сомнительны, разве что автору не зашел С++ как таковой, а что не так с IDE для плюсов?

Автор считает, что может писать достаточно быстрый, в его понимании, код на Java. И не может этого на С++.

Аргументы спорные, т.к. не нравится ручное управление памятью - берем unique_ptr или shared/weak пару, и получаем почти что Java, с несколькими оговорками: освобождение памяти будет сразу, как только ресурс не нужен и с циклическими зависимостями придется разбираться самому. Вопрос в другом: либо "задержки" не такие уж и низкие, либо на вопросе "за сколько миллисекунд гарантировано отработает new" этот код будет отправлен на доработку.

Что касается систем с низкими задержками на С++, есть отличное видео с CppCon 17, которое опосредованно дает понять, почему не Java: https://youtu.be/NH1Tta7purM и похожее от другого докладчика 19го года: https://youtu.be/_0aU8S-hFQI

Тезисно:

  • отличное практическое время принятия решения для автотрейденговой системы 17го года в районе 2.5 микросекунд. Микросекунд. Это быстрее, чем свет пройдет 1 км в вакууме. Миллисекунда - это катастрофа. 30мс из-за того, что new застрял на локе GC, система усыпила поток, потом разбудила, а в процессе еще и L1 кеши попортила - это 30 катастроф.

  • if-ы выносятся из "быстрого пути", и по, возможности, делаются в compile-time. Код без ветвлений компактно инлайнится, не занимает слишком много места в кеше и упрощает работу prefetrcher-ов и branch-prediction в процессоре. В бинарнике при этом будет несколько почти одинаковых линейных функций, одна из которых будет использоваться, а другие мирно лежать вне наша в ОЗУ.

  • много внимания уделяется состоянию кеша процессора, вплоть до того, что в случае, если система приняла решение не совершать транзакцию на раннем этапе, целесообразно пройти все стадии совершения транзакции (пусть и с нулевой суммой транзакции), чтобы держать кеши актуальными. Потому, что потом 5 наносекунд задержки на подтягивания кода из ОЗУ станут той самой задержкой, за которую конкурент успеет совершить сделку вместо вас. На этом месте мы нервно поглядываем на GC.

  • использование API системы в "горячем" пути не должно быть. Одно переключение в режим ядра может стоить десятки миллисекунд. И да, оно загрязнит кеш.

  • локальность данных очень важна для извлечения выгоды из использования кешей и prefetcher-а. Другими словами, список указателей на объекты - это катастрофа. Потому, что звенья списка лежать в одном месте ОЗУ, объекты в другом, и при этом скорее всего сами объекты лежать в лучшем случае кластерами, а в худшем вообще разбросаны по всей ОЗУ.

  • полиморфизм времени выполнения (т.е. интерфейсы и виртуальные вызовы) вносит заметные задержки. Он усложняет работу prefetcher-а, как минимум, добавляя лишнее обращение по указателю и увеличивает размер объекта на размер ссылки. Больше размер объекта - меньше локальность данных при проходе по всей коллекции.

И здесь мы приходим к тому, что если у нас система с низкими задержками, то нам нужны шаблоны, ручное управление памятью, отсутствие неявных проверок, отсутствие неявных вызовов и мнопоточной синхронизации и возможность посмотреть глазами в сгенерированный код. Другими словами, нам сразу не подходит язык, выполняемый на виртуальной машине и создающий недетерминированный код, a-la:

if (calls > MAGIC_CONSTANT) allocateNewNativeFunction(callJitCompiler(&myFunction));

Если пользователь может себе позволить задержки в миллисекунды и десятки миллисекунд, то это уже не система с низкими задержками.

Делали бы уже на FPGA систему, раз высокочастотный трейдинг скатывается к числодробилкам типа майнеров, то и решения напрашиваются такие же. На аппаратном уровне принимать решения, задержки в наносекунды, наносекунды против микросекунд у конкурентов, можно 1000 раз провести сделку, быстрее, чем у конкурентов.

Что С++, что Java тут решают не свойственные им задачи.

В целом жалко что столько сил вкладывается в решение бессмысленной задачи ВЧ трейдинга, игра с нулевой суммой на грани законности, но за рамками здравого смысла.

НЛО прилетело и опубликовало эту надпись здесь

Тоже об этом подумал, плюс возможно решение на Java дешевле, чем держать штат инженеров для поддержки FPGA.

В целом жалко что столько сил вкладывается в решение бессмысленной задачи ВЧ трейдинга, игра с нулевой суммой на грани законности, но за рамками здравого смысла.
Вы же сами сказали, что это игра. Пока у людей есть излишки ресурсов, кто-то просаживает их донатами на скинчики Fortnite и пушки CS:GO, а кто-то — в трейдинг. Имеют право.

Плюс есть некоторый полезный выхлоп, например эта статья. Наработки могут пригодится в управлении каким-нибудь станком, 3D принтером.

для "системы с низкой задержкой" stop the world от GC фатален. И избавляться от него в джаве дороже, чем писать на плюсах...

смотря какой у вас софт и железо. программы на java могут вообще без stop the world работать от запуска до финиша. так что не стращайте

Могут, в одном из следующих случаев:

  1. программа работает недостаточно долго чтобы инициировать GC до окончания. Это совершенно не наш случай.

  2. используя инкрементальные или параллельные алгоритмы. В первом случае вводится куча мелких STW, однако достаточно больших, чтобы как минимум ставить под сомнение применимость в latency-critical приложениях. Во втором случае вводятся дополнительные синхронизации потоков, чего мы в latency-critical приложениях тоже не хотим.

  3. Управляем памятью вручную. По мне так это лучше делать в языке, изначально под это заточенным.

Не поймите меня неправильно: я верю, что джава используется в финтехе. Но я не верю что она сколь угодно распространена там, где задержки критически важны.

ничёсе страсти. как бы ничего не мешает вам программировать на java создавая минимум мусора, а ваша многопроцессорная система отлично будет собирать его с микроскопическими фризами основного процесса. ну и есть разные сборщики, в том числе очень продвинутые типа ZGC. А вместо плюсов можно и Rust использовать. Как бы не зря крупные брокеры пишут на java, эти деньги из рук никогда не выпустят и им плевать на моду и холивары, ну и второй тоже не просто так появился и разрабатывается. Короче не понятно только что с c++ делать :)

Простите, я правильно понимаю ваш мессадж? Java быстрее C++ потому что разработка на нем быстрее и проще. Но тогда Python получается ещё быстрее, ведь он проще Java.

Очень много слов, мало смысла.

Вообще ответ на вопрос освещаемый в статье прост. И вытекает из вполне линейной логи

  1. Если вы взялись писать систему с низкими задержками, можно предположить что у вас есть компетенции в этом вопросе и вы понимаете что команда студентов не сделает это на том де уровне что и специалисты с большим опытом.

  2. Специалист с опытом вряд-ли будет допускать ляпы описанные в этой статье, потому что хорошо знаком со спецификой языка и это никак не зависит от выбранного языка разработки.

  3. Вытекает из второго само собой, чем ниже язык к аппаратной части и меньше зависит от GC тем эффективнее будет решение.

Очень много слов, мало смысла.

Вообще ответ на вопрос освещаемый в статье прост. И вытекает из вполне линейной логи

  1. Если вы взялись писать систему с низкими задержками, можно предположить что у вас есть компетенции в этом вопросе и вы понимаете что команда студентов не сделает это на том де уровне что и специалисты с большим опытом.

  2. Специалист с опытом вряд-ли будет допускать ляпы описанные в этой статье, потому что хорошо знаком со спецификой языка и это никак не зависит от выбранного языка разработки.

  3. Вытекает из второго само собой, чем ниже язык к аппаратной части и меньше зависит от GC тем эффективнее будет решение.

Написать хороший код которы будет работать быстро можно на обоих языках, но когда встанет вопрос найти разработчиков и поддержки кода, java выигрывает сильно

Зарегистрируйтесь на Хабре, чтобы оставить комментарий