Все разработчики знают, что есть два способа сделать дело: первый — вручную, медленно, нервно, сложно, либо второй – автоматизировано, быстро и еще сложнее.

Например, я мог бы продолжить писать эту статью о том, почему стоит использовать Java вместо C++ при программировании систем с низкой задержкой. Либо мог бы обучить ИИ, чтобы он сделал это за меня. Второй подход, в конце концов, сэкономил бы мне массу времени – искусственный интеллект генерировал бы за меня тысячи статей в секунду – но редактор вряд ли обрадовался бы услышать, что на подготовку первой статьи мне нужно два года.

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

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

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

Все сводится к тому, как вы понимаете «низкую задержку». Сейчас объясню…

Народная мудрость

Сначала рассмотрим, по каким причинам C++ предпочтителен для создания высокоскоростных систем с низкой задержкой. 

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

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

Итак, в чем же дело?

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

(Реальные) отличия между Java и C++

Но такая проблема с длительностью разработки – это еще цветочки по сравнению с реальными отличиями между Java и C++ в боевых системах. Итак, чтобы понять истинную ценность каждого из языков в этом контексте, давайте немного развернем этот момент.

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

Суть всего этого такова: если вы в самом деле не владеете С++ в совершенстве (а это навык, на овладение которым уходят десятки лет), то код на C++ потребуется отлаживать часами, если не неделями. Любой, кто пытался отлаживать движок Монте-Карло или решатель для уравнений в частных производных (PDE), расскажет вам, что отладка памяти на фундаментальном уровне может занимать невообразимое количество времени. Всего один дефектный указатель может легко обвалить всю систему, так что сдача новой версии программы, написанной на C++ - это иногда просто ужас.

Это, конечно же, еще не все. Те, кто наслаждается программированием на C++ (все трое) укажут вам, что сборщик мусора в Java страдает от нелинейных всплесков задержки. Это, в частности, актуально при работе с унаследованными системами. Поэтому добавление обновлений в код Java так, чтобы не сломать клиенту всю систему, может оказаться медленным вплоть до неприменимости.

На что я возражу, что за последнее десятилетие проделана огромная работа, чтобы снизить задержку, возникающую из-за сборщика мусора Java. Например, есть LMAX Disruptor, трейдинговая платформа с низкой задержкой, написанная на Java, которая строилась как каркас для «сродства с железом» (mechanical sympathy), на котором она работает – причем, без блокировок. 

Проблемы можно дополнительно сгладить, если вы пишете систему, в которой используется процесс непрерывной интеграции и доставки (CI/CD), потому что CI/CD позволяет автоматически развертывать протестированные изменения кода. Дело в том, что CI/CD позволяет пошагово подходить к снижению задержки на уровне сборщика мусора, благодаря чему Java можно постепенно улучшать и подгонять к конкретным аппаратным окружениям без ресурсозатратной подготовки кода в соответствии с разными аппаратными спецификациями, что пришлось бы заблаговременно делать перед его сдачей. 

Поскольку IDE в Java поддерживаются гораздо полнее, чем в C++, в большинстве сред (Eclipse, IntelliJ, IDEA) вы сможете рефакторить Java. Это значит, что большинство IDE позволят вам оптимизировать код для снижения задержек – а при работе с C++ такие возможности до сих пор ограничены. 

Пусть Java и не сравнится с C++ по чистой производительности, большинство разработчиков могут достичь приемлемой производительности на Java гораздо легче, чем достигли бы на C++. Реально задержка убивается на отрезке «у меня есть идея – я выразил ее в коде».

Что такое «быстрее»?

На самом деле, есть немало причин задуматься, на самом ли деле С++ «быстрее» Java, и «меньше» ли в самом деле задержки на нем. Я сознаю, что здесь уже углубляюсь в дебри, и многие разработчики хотят спросить, а не брежу ли я. Но разрешите я доскажу.

Во-первых, есть (слегка абсурдная) точка зрения, что, если у вас есть два разработчика, один из которых пишет на C++, а другой на Java, и обоим вы дадите задачу написать платформу для высокоскоростного трейдинга с нуля, то Java-разработчик перейдет к трейдингу гораздо раньше, чем C++-разработчик. Те, кто программировал на обоих этих языках, понимают, почему: просто в Java гораздо реже встречается неопределенное поведение, чем в C++. Ограничусь всего одним примером: выход индекса за пределы массива – это ошибка как в Java, так и в C++. Если случайно сделать такое в C++, то можно получить ошибку сегментации или (чаще) какое-то случайное число, которое даже опытному разработчику ничего не скажет. В Java при выходе индекса за пределы массива всегда выбрасывается ArrayIndexOutOfBoundsException. Поэтому отладка в Java гораздо проще, так как все ошибки обычно вскрываются немедленно, и местоположение бага легче отследить.

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

То есть, в реальном мире Java зачастую быстрее С++, даже при стандартном подходе к изменению задержки. А там, где С++ быстрее Java, разница в задержке между языками зачастую поглощается другими факторами и становится совершенно несущественной, даже в такой области, как высокочастотный трейдинг. Например, проделана большая работа по снижению задержки в  5G-сетях — по данным некоторых аналитиков, она уменьшилась до 1 мс, но в программировании под низкие задержки и такие цифры заметно сказываются на производительности. 

Преимущества Java в системах с низкой задержкой

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

Но, чтобы еще немножечко поколебать энтузиастов C++, давайте пробежимся еще по нескольким доводам в пользу Java:

  • Во-первых, как уже было показано выше, любые излишние задержки, которые Java привносит в вашу программу, наверняка гораздо ниже, чем уже имеющиеся – например, задержки при сетевой коммуникации в (как минимум) такой системе, где сделка должна быть полностью проведена, прежде, чем завершиться. Это значит, что любой (качественный) код на Java в большинстве трейдинговых ситуаций может работать так же быстро, как и код на C++.

  • Разработка на Java требует меньше времени, и это также означает, что программы, написанные на Java, можно быстрее приспосабливать к аппаратным изменениям (или даже новаторским трейдинговым стратегиям), чем программы на C++.

  • Развивая эту мысль, увидим, что даже оптимизация программы на Java может пойти быстрее — если рассматривать ее в контексте всей программы — чем при решении эквивалентной задачи на C++. Как рассказал InfoQ Питер Лоури, Java-консультант, интересующийся низкими задержками и системами с высокой пропускной способностью, “если ваше приложение тратит 90% времени на выполнение 10% вашего кода, то на Java оптимизировать эти 10% сложнее, зато легче написать и поддерживать 90% остального кода, особенно если способности специалистов в вашей команде разнятся.”

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

Ну так что?

Если вы дочитали до этого абзаца и разрабатываете системы для трейдинга с низкой задержкой, то, вероятно, задумались, а касается ли вас все вышесказанное. Ответ (за немногочисленными исключениями) – да.

Спор о том, как добиться низкой задержки, не нов, и актуален не только в мире финансов. Поэтому здесь можно извлечь ценные уроки и для других ситуаций. В частности, приведенный выше аргумент, что Java «лучше» по причине большей гибкости, надежности, быстроты разработки и поддержки – применим во многих областях программирования.

Причины, по которым (лично я) предпочитаю писать системы с низкой задержкой на Java – те же самые, что обеспечивают успех этого языка на протяжении последних 25 лет. Код на Java легко писать, компилировать, отлаживать и изучать, а значит, вы потратите меньше времени на написание кода, и у вас останется больше времени на оптимизацию задержек. В конечном итоге, это надежно позволит создавать более быстрые трейдинговые системы. А это как раз важнее всего.

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


  1. Dima_Sharihin
    02.11.2021 15:53
    +2

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

    std::out_of_range

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

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

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


    1. netricks
      02.11.2021 15:57

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


      1. Dima_Sharihin
        02.11.2021 16:06
        +2

        Как недавно пролетала шутка с 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 из указателя на нестатическую функцию класса


        1. netricks
          02.11.2021 16:21
          +7

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


        1. dvserg
          02.11.2021 16:23
          +2

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

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


        1. apro
          02.11.2021 17:41
          +9

          Есть 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); }

          ```


        1. Kelbon
          02.11.2021 17:55
          +1

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

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

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

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

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

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

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

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

          https://godbolt.org/z/s88s681M6


        1. F0iL
          02.11.2021 20:14
          +6

          Вы, кажется, путаете понятия "рантайм" и "стандартная библиотека".


        1. Woodroof
          03.11.2021 07:52
          +1

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

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

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

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

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

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

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

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

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

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


        1. 4eyes
          03.11.2021 18:14
          +2

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

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


    1. qw1
      03.11.2021 10:25
      +1

      std::out_of_range
      Вы всегда пишете
      myvector.at(20)=100;
      вместо
      myvector[20]=100;?
      И всем советуете работать с содержимым вектора через at()?


      1. agmt
        03.11.2021 10:38

        Если не хочу проверять диапазон и хочу исключение — да.
        Иначе: используйте gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html.
        Дорогой, медленный, ABI-несовместимый способ, зато без UB.


        1. qw1
          03.11.2021 10:42

          Предлагаете собирать OpenSSL всегда в debug mode, а то вдруг через 5 лет кто-то найдет там способ выйти за пределы массива?


  1. netricks
    02.11.2021 15:56
    +1

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


  1. dendron
    02.11.2021 15:57
    +23

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


    1. Revertis
      02.11.2021 16:05
      +6

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


      1. Dima_Sharihin
        02.11.2021 16:07

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


      1. amarao
        02.11.2021 17:11
        +32

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


        1. sshikov
          02.11.2021 17:37
          +4

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


          1. aamonster
            03.11.2021 00:34

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


            1. Artyomcool
              15.11.2021 22:53
              +1

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


          1. msbic
            03.11.2021 12:15

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

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

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

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


            1. sshikov
              03.11.2021 12:25

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


              1. msbic
                03.11.2021 17:49

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

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

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


          1. dyadyaSerezha
            15.11.2021 19:47

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


        1. Revertis
          02.11.2021 17:38
          -2

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

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


          1. amarao
            02.11.2021 18:01

            Это кто вам рассказывал про отсутствие 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.


            1. Revertis
              02.11.2021 18:07

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


              1. amarao
                02.11.2021 18:26
                +12

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

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


            1. Artyomcool
              17.11.2021 14:08

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

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


              1. amarao
                17.11.2021 14:39

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


                1. Artyomcool
                  17.11.2021 17:14

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

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

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


                  1. amarao
                    17.11.2021 17:37

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


                    1. Artyomcool
                      17.11.2021 17:44

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

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

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


                      1. amarao
                        17.11.2021 17:51

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


                      1. Artyomcool
                        17.11.2021 17:56

                        То, что вы говорите, называется 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.


  1. k-morozov
    02.11.2021 16:26
    +12

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


    1. amarao
      02.11.2021 17:09
      +1

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


  1. Kelbon
    02.11.2021 16:43
    +19

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

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

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

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

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


    1. Chronicler
      02.11.2021 22:01
      +1

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

      P. S.

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

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


    1. 0xd34df00d
      02.11.2021 22:17
      +2

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


      ИМХО лучше всего написать DSL и генерировать что-нибудь ассемблерное по нему явно.


    1. AnthonyMikh
      03.11.2021 13:15

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

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


      1. Kelbon
        03.11.2021 13:35
        +1

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


        1. AnthonyMikh
          03.11.2021 14:22

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


          1. Kelbon
            03.11.2021 16:52
            -3

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


  1. amarao
    02.11.2021 16:50
    +14

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


    1. Shatun
      02.11.2021 17:17
      -7

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

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


      1. amarao
        02.11.2021 17:21
        +8

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

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

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


        1. Shatun
          02.11.2021 17:28

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


          1. sshikov
            02.11.2021 17:40
            +5

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


            1. Shatun
              02.11.2021 17:48

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

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

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


              1. sshikov
                02.11.2021 17:57
                +2

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


                1. Shatun
                  02.11.2021 17:59

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


            1. GubkaBob
              02.11.2021 18:03
              +1

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


              1. sshikov
                02.11.2021 18:10
                +1

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

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


                1. GubkaBob
                  02.11.2021 18:19

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

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


                  1. sshikov
                    02.11.2021 18:29

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


          1. amarao
            02.11.2021 17:55
            +1

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

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


            1. Shatun
              02.11.2021 18:03

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

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


            1. sshikov
              02.11.2021 18:04
              +2

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

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


              1. SergeyNovak
                02.11.2021 18:15
                +2

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


              1. 4eyes
                03.11.2021 19:17
                +1

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

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


                1. sshikov
                  03.11.2021 19:29

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

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


                  1. zxweed
                    07.11.2021 13:32

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


                    1. sshikov
                      07.11.2021 13:58

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


                      1. zxweed
                        18.11.2021 15:38

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


                      1. sshikov
                        18.11.2021 17:19

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


            1. Ddnn
              03.11.2021 13:26

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

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


              1. amarao
                03.11.2021 13:27

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


                1. Ddnn
                  03.11.2021 13:48
                  +2

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


                1. F0iL
                  03.11.2021 21:42
                  +2

                  Ну в браузерах примерно так и происходит.
                  В Javascript GC есть и должен быть, а в самом веб-движке GC... ну, скажем так, тоже есть :)
                  И вот тут нужно вспомнить, что любой DOM-элемент со страницы (то есть какая-то сущность из веб-движка в виде C++-объекта) может так же существовать в в виде объекта мире JS-движка... Вплоть до того, что, например, скрипт на странице открепляет какой-нибудь элемент от DOM-дерева, но хранит ссылку на него в какой-то переменной - в итоге с точки зрения веб-движка ссылок на объект не осталось и можно бы его удалить, а на самом деле нельзя. И наоборот.
                  И ничего, справляются нормально. Вот тут об этом разработчики рассказывали:


      1. DistortNeo
        02.11.2021 17:40
        +1

        весь C++ на джаву.

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


    1. YuryB
      04.11.2021 21:27
      -2

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

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


      1. DistortNeo
        04.11.2021 21:34
        +3

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

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


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

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


        1. sergegers
          05.11.2021 01:15
          +1

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

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


      1. Antervis
        05.11.2021 01:32
        +1

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

        QtCreator же


      1. amarao
        05.11.2021 13:14
        +1

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


  1. Sergey_zx
    02.11.2021 17:18
    +8

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

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

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


  1. PkXwmpgN
    02.11.2021 17:35
    +15

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

    К тяжелому?


    1. sergegers
      02.11.2021 19:14
      -1

      bare-metal


      1. 0xd34df00d
        02.11.2021 21:04
        +10

        Technical bare metalcore.


        1. sergegers
          02.11.2021 21:30
          -4

          1. DistortNeo
            02.11.2021 21:36
            +9

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


        1. transcengopher
          18.11.2021 15:50

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


  1. GospodinKolhoznik
    02.11.2021 17:46
    -5

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


  1. Flux
    02.11.2021 17:50
    +9

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


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


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


    1. fougasse
      02.11.2021 18:06
      +5

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

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

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


      1. YuryB
        04.11.2021 20:55
        -2

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

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

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


    1. GospodinKolhoznik
      02.11.2021 18:07

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


    1. Revertis
      02.11.2021 18:08
      -2

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


      1. Flux
        02.11.2021 19:23
        +1

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


        1. Artyomcool
          17.11.2021 17:33

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


      1. andreyverbin
        02.11.2021 19:42
        +2

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


        1. Revertis
          02.11.2021 19:46

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


          1. andreyverbin
            07.11.2021 18:47

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


            1. Revertis
              07.11.2021 18:50

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


              1. andreyverbin
                07.11.2021 19:06
                -2

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


                1. Revertis
                  07.11.2021 19:10

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

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


                  1. andreyverbin
                    08.11.2021 09:23
                    +1

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

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

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

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


                    1. Artyomcool
                      17.11.2021 17:24

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


                  1. Artyomcool
                    17.11.2021 17:23

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


                1. qw1
                  07.11.2021 19:49
                  +2

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


        1. fougasse
          02.11.2021 19:47

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


        1. sshikov
          02.11.2021 21:29

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


          1. andreyverbin
            07.11.2021 18:32
            -1

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


            1. sshikov
              07.11.2021 19:23

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


              1. andreyverbin
                08.11.2021 10:04

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

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

                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 вылезает в моменты пиковой нагрузки, когда а) система перегружена и тормозит сама по себе б) объектов выделяется много в силу большого числа запросов в) объекты живут долго, ибо система тормозит. Вот тут наступает полная опа. Одно из решений это не допускать таких режимов работы. Другое - положить множество живых объектов в пул, система будет работать лучше чем без пула, но все равно будут длительные паузы.


                1. sshikov
                  08.11.2021 10:32

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


                1. qw1
                  08.11.2021 11:30

                  Можно придумать какие-то компромисы, например не делать пул сразу на миллион объектов, а разбить его на несколько арен по 100 объектов. Кончается одна сотня — выделяется следующая. При выделении объекта приоритет отдавать минимальной арене, а пустые арены (кроме первой) удалять.

                  В идеале, библиотека пулов должна выделать память не в управляемой куче. Тогда JVM не будет знать, что выделенная память, но не отданная приложению из пула, является живым объектом, и не будет обходить его при GC. Не знаю, есть ли такое решения. Они явно unsafe и заточены под конкретную JVM.


            1. qw1
              07.11.2021 19:50
              +1

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


        1. Artyomcool
          17.11.2021 17:22

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


          1. qw1
            17.11.2021 22:52

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

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

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

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


            1. Artyomcool
              18.11.2021 00:10

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


              1. qw1
                18.11.2021 09:51

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


                1. Artyomcool
                  18.11.2021 10:43

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


                  1. qw1
                    18.11.2021 13:28

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


                    1. Artyomcool
                      18.11.2021 13:45

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


                      1. qw1
                        18.11.2021 15:18

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

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


                      1. 0xd34df00d
                        18.11.2021 18:47
                        +1

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


                      1. qw1
                        18.11.2021 19:13

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

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


                      1. transcengopher
                        18.11.2021 20:42

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


                      1. qw1
                        18.11.2021 22:57

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

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


                      1. transcengopher
                        18.11.2021 23:47

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


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

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


                      1. qw1
                        18.11.2021 23:58

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


                      1. Artyomcool
                        19.11.2021 18:56

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


                      1. qw1
                        20.11.2021 02:14

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


                      1. Artyomcool
                        20.11.2021 04:52

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

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

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

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


                      1. qw1
                        20.11.2021 17:40

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

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


                      1. Artyomcool
                        21.11.2021 01:09

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

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

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


                      1. Artyomcool
                        19.11.2021 18:54

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

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


                      1. qw1
                        20.11.2021 02:21

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


                      1. Artyomcool
                        19.11.2021 18:48

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

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


                      1. qw1
                        20.11.2021 02:18

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


                      1. Artyomcool
                        19.11.2021 18:50

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

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

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


                      1. qw1
                        20.11.2021 02:20

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


    1. Shatun
      02.11.2021 18:55
      +4

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

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


      1. YuryB
        04.11.2021 20:27

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


    1. andreyverbin
      02.11.2021 19:35

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


    1. GospodinKolhoznik
      02.11.2021 21:45
      +3

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

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


      1. andreyverbin
        08.11.2021 10:14

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


    1. sim31r
      05.11.2021 13:21

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


  1. screwer
    02.11.2021 18:56
    +3

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


    1. sim31r
      05.11.2021 13:23

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


      1. akhmelev
        05.11.2021 19:08

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

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


        1. sim31r
          07.11.2021 00:01

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

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


  1. Tujh
    02.11.2021 19:54
    +6

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

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

    Например, есть 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?


    1. sshikov
      02.11.2021 21:26

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

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

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


  1. oleg-m1973
    02.11.2021 20:29
    +5

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


    1. GospodinKolhoznik
      02.11.2021 21:32

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


      1. 0xd34df00d
        02.11.2021 22:19
        +6

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


        В этом нашем HFT мы боролись за десятки, а в горячих местах и за единицы наносекунд. 1 мс — это вечность.


        1. GospodinKolhoznik
          02.11.2021 22:48
          -1

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

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


          1. 0xd34df00d
            02.11.2021 23:15
            +3

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

            Кого именно? Трейдеров, которые там успешно (до кризиса 2020-го, по крайней мере) торговали? Или людей, которые несли свои деньги в фонд и получали обратно существенно выше инфляции?


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

            Именно так.


            Только это не информация, а белый шум.

            На белом шуме много не наторгуешь, в отличие от.


        1. enree
          03.11.2021 01:21
          -1

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


          1. 0xd34df00d
            03.11.2021 02:09
            +2

            Откуда там миллисекунды?


            Если что, речь о том случае, когда торгующие сервера физически рядом с биржей.


        1. Artyomcool
          18.11.2021 00:39

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


      1. YuryB
        04.11.2021 20:33

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


  1. SirEdvin
    02.11.2021 21:54
    +1

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


  1. light_and_ray
    02.11.2021 22:38

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

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


    1. 0xd34df00d
      02.11.2021 23:16
      +1

      Это, видимо, dead code elimination.


      1. light_and_ray
        02.11.2021 23:20

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


        1. 0xd34df00d
          02.11.2021 23:25
          +2

          Если у вас написано


          const bool flag = ReadFromEnv("FLAGNAME");
          if (flag)
            DoSmth();
          else
            DoSmthElse();

          то одна из этих веток — мёртвый код, но вы не знаете до запуска, какая.


          Правда, совершенно не факт, что автор именно это имел в виду.


          1. sshikov
            03.11.2021 19:33

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


    1. light_and_ray
      02.11.2021 23:31

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


      1. igormich88
        03.11.2021 10:54

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


    1. akhmelev
      05.11.2021 19:15

      Hotspot так называется потому что.... (и дальше ответ на ваш вопрос)


  1. IGR2014
    03.11.2021 06:41

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


  1. Ingulf
    03.11.2021 13:39
    +2

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


  1. 4eyes
    03.11.2021 19:05
    +3

    Автор считает, что может писать достаточно быстрый, в его понимании, код на 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));

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


    1. sim31r
      05.11.2021 13:31
      +1

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

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

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


      1. 0xd34df00d
        05.11.2021 19:02
        +1

        FPGA там тоже делаются, но нередко бывает, что FPGA принимает решение на базе достаточно простой модели, параметры которой регулярно тюнятся по команде с большой x86-машины (условно, сложная модель раскладывается в Тейлора, и берется пара старших коэффициентов). Тогда чем быстрее вы обновляете сложную модель, тем лучше.


        1. sim31r
          06.11.2021 23:17

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


      1. qw1
        05.11.2021 21:56

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


        1. sim31r
          06.11.2021 23:22

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


  1. Antervis
    04.11.2021 02:01
    +1

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


    1. YuryB
      04.11.2021 20:20

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


      1. Antervis
        04.11.2021 21:34

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

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

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

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

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


  1. YuryB
    04.11.2021 20:09

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


  1. bakeneko1
    04.11.2021 20:46

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


  1. OkunevPY
    04.11.2021 20:46
    +1

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

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

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

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

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


  1. OkunevPY
    04.11.2021 20:46
    -1

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

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

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

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

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


  1. AlexanderAlexandrovich
    20.11.2021 18:24

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