Обновить

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

Если заменить sleep чем-то реальным, например обращению к бд, или рест запросом к другому приложению, то картинка резко перестанет быть настолько радужной.

Спасибо за комментарий.

Если заменить обращением к БД, то да, картинка изменится, а если рест запросом, то нет. И непонятно с чего бы ей меняться.

а если рест запросом

Изменится конечно

Изменится конечно

А отчего? В чём там характерные отличия от Thread.sleep?

Thread.sleep просто таймер, он не задействует ресурсы ОС, а рест это I/O, и независимо от типа потока (реальный, виртуальный), такая операция жрет ресурсы. При отправки запроса происходит примерно следующее:
– устанавливается TCP-соединение, которое занимает один порт и один файловый дескриптор
– отправляется запрос
– сокет переходит в состояние ожидания, а ось активно отслеживает его, ожидая ответа
то есть, железо занято реальной работой. Это не то же что таймер.

Кроме того, количество портов и дескрипторов ограничено. В статье речь про обработку 100 000 запросов, а это 100 000 открытых сокет соединений, что невозможно, потому что портов всего около 65 тысяч, а для соединений вообще выделяется пул из примерно 28 000. Но и это еще не все, так как после закрытия TCP соединения порт блокируется по TIME_WAIT (линукс 60 сек, про другие ос не знаю).

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

рест это I/O, и независимо от типа потока (реальный, виртуальный), такая операция жрет ресурсы

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

Хотя я тут задумался и чего-то перестал понимать откуда в статье вообще взялось ускорение. У томката 200 потоков, их число не менялось. Какая разница виртуальные они или настоящие. Как я понимаю смысл виртуальных потоков, что их можно сделать больше, но тут в обоих случаях 200 вроде...

Кроме того, количество портов и дескрипторов ограничено

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

TCP соединения порт блокируется по TIME_WAIT (линукс 60 сек, про другие ос не знаю)

Можно не закрывать ))

А причём тут бд? Речь в статье про слой веб сервиса. Если у вас cassandra например или шардированный postgres, то все хорошо будет. Если конечно вы упираетесь в ресурсы бд или другого сервиса то конечно ничего хорошего не будет. Но в этом случае ничего хорошего не будет с любым яп и любым фреймворком.

Согласен

Важно отметить, что если виртуальный поток выполняет код внутри блока synchronized, он присоединяется к потоку-носителю.

Виртуальные потоки не будут давать преимущества, если они выполняют синхронизированные блоки или вызывают собственные методы.

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

И да, делать выводы по Thread.sleepтакое себе, можно не полениться и все-таки хотя бы побросать запросы на какой никакой сервер.

Спасибо за комментарий и советы.

Когда ждать обновления тестов с учетом замечаний? 😉😀

😀😀😀 Как только появится свободное время.

Очередная дичь подъехала.

Для обычных будем использовать FixedThreadPool с количеством потоков 1000. ....

То есть виртуальные потоки справились в 3,7 раза быстрее обычных в данном примере.

А почему не Executors.newFixedThreadPool(1)? тогда бы виртуальные потоки были бы в тысячи раз быстрее! Для адекватного теста надо тогда уж делать Executors.newCachedThreadPool() что бы лимит на создание как обычного потока так и виртуального был максимален как у вас в newVirtualThreadPerTaskExecutor.

Но это не суть.. Виртуальные потоки это не про скорость, а про утилизацию ресурсов!
Суть в том что из одной и той же железки вы сможете выжать больше пропускной способности в случае виртуальных потоков!
В тесте с томкатом такая же ошибка, вы уперлись в его веб-пул на 200 потоков и делаете выводы - хотя вообще не понятно какая у вас загрузка ЦПУ и памяти.
Тест должен быть такой: вы должно увеличивать число потоков его веб-пула до тех пор, пока не упретесь либо в ЦПУ либо память - и после этого сделать вывод, что имея сктолько то ЦПУ и столько то ОП, мы смогли выжать из синхронного апи, при таком-то размере его входного пула - такую то пропускную способность, пока не уперлись либо в проц или память. И далее для такой же "железки" повторить тест с виртуальными потоками - пока не упретесь в ресурсы "железки", и да - если все сделано правильно, то пропускная способность там будет выше - т.е. "железка" используется более рационально и ее возможности более полно "утилизируются", а не простаивают.
Если вы хотите доказать что виртуальный поток, быстрее обычного, то вы должны сравнить скорость выполнения одного виртуального потока с обычным и после этого делать вывод. Спойлер: у вас ничего не получится). А потому что не поток становится быстрее, а система становится не блокирующей(если вы конечно нигде не выстрелите себе в ногу как в случае с синх.драйвером к БД как сказали выше) и дальше все то что я написал выше.

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

Судя по https://tomcat.apache.org/tomcat-10.1-doc/config/executor.html ответ - вместо пула ограниченного размера будет использоваться Executor, создающий виртуальный поток на каждый коннект

А ещё можно научиться в Reactor Project.

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

Я плохо знаю виртуальные потоки, у меня вопрос к спецам. Какая архитектура виртуальных потоков: 1. Потоки запускаются одновременно и переключаются роунд-робин средствами jvm выполняя код "одновременно", 2. или скажем, при получении запросов hhtp, запрос создает виртуальный поток, тот встает в очередь, а четыре (условно) реальных потоков, закончив задание текущего виртуального потока, подбирает следующий поток из очереди и выполняет его, переключаясь на следующий, если текущий виртуальный поток ждет ИО операцию?

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

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

Всем привет! Статья доработана с учетом предложенных советов.

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

Публикации