Для задач, потребляющих ЦП, или непотребляющих, выгодно ли всегда использовать параллельный поток для оптимизации производительности?
Абсолютно нет. Рассмотрим этот пример:
Flux.range(1, 10)
.parallel(4)
.runOn(Schedulers.parallel())
.sequential()
.elapsed()
.subscribe(i -> System.out.printf(" %s ", i));
Приведенный выше код является пустой тратой, потому что i
будет обработан почти мгновенно. Следующий код будет работать лучше, чем приведенный выше:
Flux.range(1, 10)
.elapsed()
.subscribe(i -> System.out.printf(" %s ", i));
Теперь рассмотрим это:
public static <T> T someMethodThatBlocks(T i, int ms) {
try { Thread.sleep( ms ); }
catch (InterruptedException e) {}
return i;
}
// some method here
Flux.range(1, 10)
.parallel(4)
.runOn(Schedulers.parallel())
.map(i -> someMethodThatBlocks(i, 200))
.sequential()
.elapsed()
.subscribe(i -> System.out.printf(" %s ", i));
Результат похож на:
[210,3] [5,1] [0,2] [0,4] [196,6] [0,8] [0,5] [4,7] [196,10] [0,9]
Как вы можете видеть, первый ответ пришел через 210
мс, за ним последовали 3 ответа с примерно 0
прошедшим временем между ними. Цикл повторяется снова и снова. Здесь вы должны использовать параллельный поток. Обратите внимание, что создание большего количества потоков не гарантирует производительности, потому что, когда количество потоков больше, переключение контекста добавляет много накладных расходов, и, следовательно, код следует протестировать задолго до развертывания. Если имеется много блокирующих вызовов, наличие более одного потока на ЦП может дать вам повышение производительности, но если выполняемые вызовы интенсивны по ЦП, то наличие более одного потока на ЦП замедлит производительность из-за переключения контекста.
В общем, это всегда зависит от того, чего вы хотите достичь.