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

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

Виртуальные потоки не дадут никакого ускорения в типичном джава приложении без переписывания кода

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

Скорее простой программист их даже не заметит. Они будут переписаны где то под капотом того же Springboot/Netty/Quarkus/etc

не забывайте, что это превью фича и очень многое еще может поменяться. Кроме того, в JEP 425 нет задачи, чтобы код переписанный на virtual threads работал быстрее. Речь идет о замене парадигмы разработки на thread-per-request при минимальном переписывании кода приложения. Что в теории должно сильно упростить разработку и дебаг.

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

Утверждение основывается на бенчмарке с Blackhole.consumeCPU?

Открываем блог-пост на сайте Oracle Coming to Java 19: Virtual threads and platform threads:

It’s important to understand what virtual threads are for—and what they are not for.

Never forget that virtual threads aren’t faster threads. Virtual threads don’t magically execute more instructions per second than platform threads do.

What virtual threads are really good for is waiting.

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

Короче, утверждение про "никакого ускорения в типичном джава приложении" спорно. Хотя бы потому что не очень понятно что такое "типичное джава приложение". Числодробилка? Прослойка между фронтом и БД?

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

Я считаю типовым приложением любую АПИшку с разной степенью навороченности бизнес логики в ней. Или любой фоновый обработчик который проводит большую часть времени на IO.

Цитата из блога Оракла говорит ровно тоже самое. Не ждите что ваш код просто станет быстрее работать. Я в статье нашёл на каких порядках цифр появляется разница. Для потоков висящих на IO результат будет таким же. Тут время висения в одном потоке важно, а не то на что оно ушло.

Цитата из блога Оракла говорит ровно тоже самое. Не ждите что ваш код просто станет быстрее работать.

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

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

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

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

Очень важно на что оно ушло. Если тред занят работой, то мы ничего с ним сделать не можем, а вот если тред просто ждёт, то его можно переиспользовать для другой работы. В этом и есть суть Loom.

https://github.com/quarkusio/quarkus/pull/24942 вот тут был другой эксперимент по переводу текущего фрэймворка.

Ребята результаты не анонсируют, но из личных разговоров в некоторых местах стало лучше на несколько нулей.

Цифры приводить не буду, лучше дождаться официального релиза

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

Я не понимаю ни как провести этот тест ни каких других результатов вы от него ждете.

Схема теста: Есть у нас пул из 100 потоков в которых IO bound задачи. Для теста сделаем так что они 100% времени висят на IO. Когда они доработают? Как IO отпустит, так сразу и отработают. Насколько велика будет разница в расходе ресурсов в виртуальных и в реальных потоках? Зависит от скорости отработки IO. Она будет ровно такая же как в мое CPU bound тесте. Почему бы ей быть другой?

Очень важно на что оно ушло. Если тред занят работой, то мы ничего с ним сделать не можем, а вот если тред просто ждёт, то его можно переиспользовать для другой работы. В этом и есть суть Loom.

Конечно. Но как это определение превратить в реальные цифры пользы совершенно непонятно. Сколько там будет той пользы тоже непонятно. Это я и попробовал сделать.

Зависит от скорости отработки IO. Она будет ровно такая же как в мое CPU bound тесте. Почему бы ей быть другой?

Если я правильно понимаю, то механизм работы IO абсолютно разный. В случае с настоящими тредами IO системное, тоесть замораживается и размораживается системный поток, что ведет например к некоему промежутку между реальным наступлением события и реальной разморозкой, в случае же virtual thread там может быть busy loop (по крайней мере, когда есть какие-либо еще задачи)

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

Ситуация: у вас 2 треда в пуле (просто ради примера). Приходит первый пользователь и ваша "тяжелая апишка с неплохим входящим РПС" занимает тред тем, что он ждёт ответа от БД. Приходит второй пользователь и занимает второй тред. Теперь приходит третий пользователь и отваливается, т.к. некому его обслужить - все треды заняты. Ну как заняты - не делают ничего и тупо висят на IO.

Теперь подключаем Loom с виртуальными тредами и, магическим образом, третьего пользователя есть кому обслужить - этим займётся один из двух тредов, который ждёт IO. И ни какого "Придется код писать". Вся магия внутри фреймворка/библиотеки, а код остаётся тем же последовательным и блокирующим, но только с виду. Look Ma, no callbacks!

Вот такой тест и стоит сделать, а не "так что они 100% времени висят на IO". Типичное джава приложение.

Делаем пул в 100 потоков. Если мало делаем 200. Это правда много и хватит на большой РПС. Это уже так сделано и работает в типичных http серверах.

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

Без переписывания кода эта трансформация не даст ничего. Если взять медиану работы быстрого апи за 5мс, то по моим измерения там все в пределах погрешности. С переписыванием (веб сервера для использования бонусов виртуальных потоков) может стать лучше. Надо код писать.

Пул в 100 потоков будет постоянно переключать контекст каждого потока туда-обратно, если у вас конечно не сервер с 100 ядров. А Loom на теже 100 виртуальных потоков (и 16 реальных например, интересно, сколько реальных тредов выделяет Loom для работы) устранит переключение контекста и только за счет этого можно будет выиграть сколько-то

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

Стоимость переключения контекста на длинных задачах переоценена. Миллисекунды это много.

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

Этим занимаются создатели фреймворков/библиотек. Ваш код с бизнеслогикой остаётся тем же. Код писать не надо.

Мой посыл в том, что в своём сравнении вы проигнорировали самую суть Loom.

Я про суть и пытался.

На каких характерных цифрах получается ускорение? В рекламе все идеально. Миллион виртуальных потоков и все летает.

А вот в реальности в пуле из пары сотен потоков и временем в каждом потоке миллисекунд 10 как-то ничего не заметно.

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

А вот в реальности в пуле из пары сотен потоков и временем в каждом потоке миллисекунд 10 как-то ничего не заметно.

Может быть к релизу что-то изменится

Я извиняюсь, а какая у вас цель? Чтобы в виртуальном потоке таска на 10 мс исполнялась быстрее, чем за 10 мс?

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

https://wiki.openjdk.org/display/loom/Getting+started

Virtual threads are
best suited to executing code that spends most of its time blocked,
waiting for data to arrive on a network socket or waiting for an element
in queue for example.

Все кейсы из вашей статьи не об этом, к сожалению.

Дописал. Спасибо за идею что еще надо проверить.

Ищите по словам "Следующий тест производительности добавлен по просьбе из комментариев "

Так sleep надо внутри виртуального треда вызывать, а не снаружи.

Спасибо. Поправил.

Не оттуда скопипастил код вечером.

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

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

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

А как же vert.x в java? В принципе он эту проблему решает. И в том же кваркус живёт под капотом 2мя слоями ниже.

Да на уровне jdk это конечно лучше, но всеж

Вы не поверите, даже я свой костыль для этого делал https://github.com/akurilov/fiber4j

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

Не, в IT костыли наше все, но...

А зачем? Когда vert.x давно существует. А smalrye оборачивает его в нормальное API вместо матрёшки в матрешках.

Может быть потому что это немного другое. Для моих целей Vert.x был неприменим

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

Миллион да. Я сейчас посмотрел на свой прод и там максимум это около 2.000 потоков на приложеньку. Очень большую и нагруженную приложеньку.

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

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

В теории да, на практике нет.

Ждущий поток не стоит почти ничего. 64 килобайта памяти. Тот же jetty на нагруженной апишке отлично живет с пулом в 100 потоков. Это тяжелая апишка с неплохим входящим РПС.

Ну и возможность отскалироваться накликав еще по ядру на каждый шард стоит дорого. Она почти всегда полезнее процента производительности от подгона потоков под ядра.

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

Полностью согласен. Уже понятно как фичу сделать правильно значит можно ее и в Джаву затаскивать.

Стоило ли это делать 5 лет назад? Не уверен.

А 10 лет назад? Точно нет.

Собственно остается непонятным, сколько реальных тредов работает в Executor-е для виртуальных тредов, и где это подкручивается. Также покрыта мраком работа планировщика: имеет ли он ввиду Thread.priority, когда несколько виртуальных потоков одновременно "просыпаются", и где можно ловить и мониторить очередь потоков ожидающих выполнения, когда все реальные треды заняты...

64 килобайта * 1_000 типовых потоков, это неинтересно

Ну вот, сначала 64 Mb - неинтересно, а потом удивляетесь, почему Java за прожорливость недолюбливают.

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

https://docs.oracle.com/en/java/javase/20/core/virtual-threads.html#GUID-15BDB995-028A-45A7-B6E2-9BA15C2E0501

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

Публикации

Истории