1) Еще нет такой информации, но вообще гипервизор должен давать не существенный оверхед, проблема скорее в том, что железо шарится между несколькими виртуалками
2) В 2 раза не пробовали увеличить и возможности такой пока нет, но мы исходим из того, что при текущем allocation rate = 10 MB/sec, свободных 4-х GB должно хватать. Мы возлагаем надежды на Shenandoah, возможно поможет, если из коробки не поможет, будем тюнить его как сможем
>> почему бы тогда не поднять несколько экземпляров сервиса и не пусть трафик через балансировщик
мы примерно так и делаем, есть несколько одинаковых расчетных сервисов, на уровне нашего клиента (джарника с grpc) принимается решение, к какому инстансу подключиться
Спасибо большое за примеры! Очень полезное дополнение.
>> а вы его обозвали недостатком!
я скорее про сравнение с синхронным кодом:
res1 = calc1()
res2 = calc2()
sendResult(res1 + res2)
В случае если переписать на сообщения, то код станет чуть размазаннее, хотя для вашего примера согласен, что акторы удобнее.
Я правильно понимаю, что в вашем коде акторы создаются динамически, на разные состояния (во втором примере)? Существенный ли оверхед на это? (мы стараемся акторы динамически не создавать и кешировать их)
Я специально в статье привел пример CalculationActor, т.к. в нем есть код по обработке входящих сообщений в createReceive() и onCalculate, и отправки результата в том же onCalculate методом sender.tell.
Код клиента можно посмотреть здесь — в нем отличие в методе preStart, где планируется отправка сообщений каждые 100 ms (эмулирется нагрузка и непрерывная работа).
>> реактивность от которой отказались, как я понял, именно в угоду читаемости.
я бы сказал, что «не перешли», а не «отказались».
Shenandoah очень хотим попробовать, но у нас есть ограничения по версии jdk, которую можем использовать в продакшене, мы пока на 11, как только появится возможность перейти на 14 Java или выше, обязательно попробуем.
Zgc нет не пробовали.
С GraalVm, в бесплатной версии не оптимально сделано выполнение simd вычислений, что для нас важно, а платную версию не было возможности протестировать.
Кстати, если вам интересна тема ухищрений на Java для высокопроизводительных вычислений, то возможно будет интересен доклад https://youtu.be/QV-ue1YMdds
>> 6 раз независимо друг от друга
Да, правильно, расчеты независимые. 1,5 ms на один расчет. И мы, конечно же, выполняем эти расчеты параллельно.
>> Для параллельного вычисления изначально и задействовали akka. Так?
Сами расчеты, два вложенных цикла, как раз то место, где akka не используется. Итерации по циклам разбиваются на батчи и обсчитываются параллельно на ForkJoinPool. На выходе получается 0,3 ms вместо 1,5. Почему не akka? Это как раз тот кейс, где акка не очень удобна. Нужно через сообщения выстраивать машину состояний и реагировать в зависимости от посчитанных значений, ForkJoinPool в этом месте удобнее.
>> Или есть какие-то другие веские причины, чтобы использовать akka?
Не очень понял, вопрос относится непосредственно к расчетам или к проекту в целом? Про расчеты написал выше, а про проект, почему акка написано в разделе «Преимущества акторов»
Спасибо за отзыв
1. Конкретно Caffeine явно не используем. В описании к нему написано, что он используется в Akka и Spring. Если так, тогда используем неявно. А вообще, вместо разрозненных кешей мы используем спапшет данных. Снапшет — это уже преобразованные, предагрегированные данные и разложенные по обычным HashMap для быстрого поиска. Снапшет меняется только несколько раз в день, поэтому там достаточно обычных HashMap. Real-time данные раскладываем в ConcurrentHashMap
2. Нет, напрямую off-heap memory не используем. Для экономии на GC используем пулы объектов, про это напишу ниже. Статья как раз о том, что без сильных ухищрений с GC от Java можно получить хорошую производительность. Лично мое мнение, использование напрямую off-heap memory не через фреймворки и библиотеки очень опасно для проекта, начиная с того, что Unsafe рано или поздно задеприкейтят и заканчивая core дампами в продакшене. Если в проекте много мест где хочется использовать Unsafe, тогда, скорее всего, Java не лучший выбор для проекта, возможно стоит рассмотреть связку С/С++ + JNI или вынести высокопроизводительный код в отдельный процесс на том же С/С++.
3. Да используем. В расчетах с матрицами. Каждая строка матрицы это массив и эти массивы переиспользуются в расчетах. Для кеширования используем очередь из JCTools. Потоки берут преаллоцированные массивы из этой очереди и кладут их обратно после завершения вычислений.
4. Скорее особенность скиллсета разработчиков. На скалу переходить не планировали, связка Akka + Java вполне устраивает.
Немного обобщу свои ответы: в нашем проекте мы особо не экономим на аллокациях небольших объектов и позволяем гарбедж коллектору их собирать, кешируем только массивы. Работаем с данными либо через большой снапшет, либо через небольшие кеши поверх ConcurrentHashMap. Не прибегая к особым ухищрениям мы сохраняем для себя удобство разработки на Java, но жертвуем тем, что обслуживание части запросов все-таки выпадает на GC паузы (не больше 5%) и обслуживаются не за 3-4 ms а за 150-200 ms. Для нас это осознанный tradeoff.
В статье явно говорится, что для GPU используется Thrust и почему, про STL может быть и не явно, но подразумевается. Суть статьи — как быстро подступиться, т.е. без хардкорного С и CUDA девелопмента. По хорошему, тогда нужно на asm писать под конкретный процессор.
STL, т.к. многие им пользуются (хоть, он и не всегда эффективен) и не все готовы писать код, который приводите Вы. Для GPU я так же не утверждаю, что thrust самая быстрая библиотека.
>> На задаче сортировки это сравнение выглядит особенно «нелепо»
Вы не поняли, в чем суть этих графиков. Я не заявляю, что «GPU крут во всех задачах», я привожу конкретное latency для конкретного подхода. Один CPU поток, т.к. методом не сложных математических вычислений, можно прикинуть, какое latency будет, если алгоритм распарралелить. Для GPU — это примеры, от которых можно отталкиваться. Да, это сравнение «теплого» с «мягким», но вычисленный результат в итоге одинаковый и посчитано затраченное время, от которого можно отталкиваться.
>> утилизирующей «видяху» с TDP 250Вт и ценой $5000.
на консьюмерских картах можно что-то похожее получить, для проверки и написан бенчмарк. Мне самому было любопытно, что можно получить с топовой карты.
2) В 2 раза не пробовали увеличить и возможности такой пока нет, но мы исходим из того, что при текущем allocation rate = 10 MB/sec, свободных 4-х GB должно хватать. Мы возлагаем надежды на Shenandoah, возможно поможет, если из коробки не поможет, будем тюнить его как сможем
мы примерно так и делаем, есть несколько одинаковых расчетных сервисов, на уровне нашего клиента (джарника с grpc) принимается решение, к какому инстансу подключиться
>> а вы его обозвали недостатком!
я скорее про сравнение с синхронным кодом:
res1 = calc1()
res2 = calc2()
sendResult(res1 + res2)
В случае если переписать на сообщения, то код станет чуть размазаннее, хотя для вашего примера согласен, что акторы удобнее.
Я правильно понимаю, что в вашем коде акторы создаются динамически, на разные состояния (во втором примере)? Существенный ли оверхед на это? (мы стараемся акторы динамически не создавать и кешировать их)
Код клиента можно посмотреть здесь — в нем отличие в методе preStart, где планируется отправка сообщений каждые 100 ms (эмулирется нагрузка и непрерывная работа).
>> реактивность от которой отказались, как я понял, именно в угоду читаемости.
я бы сказал, что «не перешли», а не «отказались».
2) Где-то 16GB живых объектов, в основном это статические данные для расчетов.
Если уточните для каких целей спрашиваете, может быть смогу более развернуто ответить
Zgc нет не пробовали.
Кстати, если вам интересна тема ухищрений на Java для высокопроизводительных вычислений, то возможно будет интересен доклад https://youtu.be/QV-ue1YMdds
Да, правильно, расчеты независимые. 1,5 ms на один расчет. И мы, конечно же, выполняем эти расчеты параллельно.
>> Для параллельного вычисления изначально и задействовали akka. Так?
Сами расчеты, два вложенных цикла, как раз то место, где akka не используется. Итерации по циклам разбиваются на батчи и обсчитываются параллельно на ForkJoinPool. На выходе получается 0,3 ms вместо 1,5. Почему не akka? Это как раз тот кейс, где акка не очень удобна. Нужно через сообщения выстраивать машину состояний и реагировать в зависимости от посчитанных значений, ForkJoinPool в этом месте удобнее.
>> Или есть какие-то другие веские причины, чтобы использовать akka?
Не очень понял, вопрос относится непосредственно к расчетам или к проекту в целом? Про расчеты написал выше, а про проект, почему акка написано в разделе «Преимущества акторов»
1. Конкретно Caffeine явно не используем. В описании к нему написано, что он используется в Akka и Spring. Если так, тогда используем неявно. А вообще, вместо разрозненных кешей мы используем спапшет данных. Снапшет — это уже преобразованные, предагрегированные данные и разложенные по обычным HashMap для быстрого поиска. Снапшет меняется только несколько раз в день, поэтому там достаточно обычных HashMap. Real-time данные раскладываем в ConcurrentHashMap
2. Нет, напрямую off-heap memory не используем. Для экономии на GC используем пулы объектов, про это напишу ниже. Статья как раз о том, что без сильных ухищрений с GC от Java можно получить хорошую производительность. Лично мое мнение, использование напрямую off-heap memory не через фреймворки и библиотеки очень опасно для проекта, начиная с того, что Unsafe рано или поздно задеприкейтят и заканчивая core дампами в продакшене. Если в проекте много мест где хочется использовать Unsafe, тогда, скорее всего, Java не лучший выбор для проекта, возможно стоит рассмотреть связку С/С++ + JNI или вынести высокопроизводительный код в отдельный процесс на том же С/С++.
3. Да используем. В расчетах с матрицами. Каждая строка матрицы это массив и эти массивы переиспользуются в расчетах. Для кеширования используем очередь из JCTools. Потоки берут преаллоцированные массивы из этой очереди и кладут их обратно после завершения вычислений.
4. Скорее особенность скиллсета разработчиков. На скалу переходить не планировали, связка Akka + Java вполне устраивает.
Немного обобщу свои ответы: в нашем проекте мы особо не экономим на аллокациях небольших объектов и позволяем гарбедж коллектору их собирать, кешируем только массивы. Работаем с данными либо через большой снапшет, либо через небольшие кеши поверх ConcurrentHashMap. Не прибегая к особым ухищрениям мы сохраняем для себя удобство разработки на Java, но жертвуем тем, что обслуживание части запросов все-таки выпадает на GC паузы (не больше 5%) и обслуживаются не за 3-4 ms а за 150-200 ms. Для нас это осознанный tradeoff.
Цифр не хватает, какая разница по скорости на практике получается.
STL, т.к. многие им пользуются (хоть, он и не всегда эффективен) и не все готовы писать код, который приводите Вы. Для GPU я так же не утверждаю, что thrust самая быстрая библиотека.
>> На задаче сортировки это сравнение выглядит особенно «нелепо»
Вы не поняли, в чем суть этих графиков. Я не заявляю, что «GPU крут во всех задачах», я привожу конкретное latency для конкретного подхода. Один CPU поток, т.к. методом не сложных математических вычислений, можно прикинуть, какое latency будет, если алгоритм распарралелить. Для GPU — это примеры, от которых можно отталкиваться. Да, это сравнение «теплого» с «мягким», но вычисленный результат в итоге одинаковый и посчитано затраченное время, от которого можно отталкиваться.
>> утилизирующей «видяху» с TDP 250Вт и ценой $5000.
на консьюмерских картах можно что-то похожее получить, для проверки и написан бенчмарк. Мне самому было любопытно, что можно получить с топовой карты.
800 MB — 229.604 ms
4,5 GB — 1133.35 ms
github.com/tishden/gpu_benchmark/blob/master/cuda/benchmark_results_for_double_64_tesla_v100.txt