Оптимизация потоков в Java при 100% загрузке процессора
=======================================================

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

Чтобы максимально быстро выполнить большую часть работы: лучше ли мне просто запускать больше потоков, когда мне нужно выполнить больше работы, и позволить планировщику потоков Java управлять распределением работы, или если бы он стал умнее и управлял рабочей нагрузкой, чтобы поддерживать процессор ниже 100% уведут меня быстрее?

Машина посвящена моему Java-приложению.

**EDIT:**

Спасибо за фантастический вклад!

Задачи имеют различную сложность и включают в себя операции ввода-вывода, поэтому наличие пула с низким потоком, скажем, 4, может привести к тому, что процессор будет работать только на 20%. У меня нет возможности узнать, сколько задач на самом деле приведет процессор к 100%.

Я подумал, должен ли я контролировать ЦП через RMI и динамически набирать объем работы вверх или вниз, или мне просто все равно, и пусть ОС справится с этим.

 требующих большого объема вычислений, в параллельных потоках, вы очень быстро достигнете точки уменьшения отдачи. Фактически, если имеется N процессоров (ядер), то вам не нужно больше N таких потоков. Теперь, если задачи иногда приостанавливаются для ввода-вывода или взаимодействия с пользователем, то правильное число может быть несколько больше. Но в целом, если в какой-то момент существует больше потоков, которые хотят выполнять вычисления, чем имеется доступных ядер, то ваша программа тратит время на переключение контекста, т. Е. Планирование стоит вам.



 но с минимальным количеством потоков. 100 потоков выглядит слишком много.



 что ваш процессор работает на 100%, мало что говорит о том, насколько они заняты**useful** Работа. В вашем случае вы используете больше потоков, чем ядер, поэтому 100% включает некоторое переключение контекста и использует излишнюю память (небольшое влияние на 100 потоков), что является неоптимальным.

Для задач с интенсивным использованием процессора я обычно использую эту идиому:

Использование большего количества потоков, как указали другие, только вносит ненужное переключение контекста.

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

**EDIT**

Чтобы ответить на комментарий @MartinJames, я провел (упрощенный) бенчмарк - результат показывает, что переход от размера пула = количество процессоров + 1 до 100 лишь незначительно снижает производительность (давайте назовем это 5%) - переходя к повышению цифры (1000 и 10000) действительно сильно влияют на производительность.

Results are the average of 10 runs:  

 Pool size: 9: 238 ms. //(NUM\_CORES+1)  

 Pool size: 100: 245 ms.  

 Pool size: 1000: 319 ms.  

 Pool size: 10000: 2482 ms. 

Код:



 чтобы поддерживать процессор ниже 100%, помогло бы мне быстрее?"

Возможно нет.

Как уже сообщали другие, 100 потоков - это слишком много для пула потоков, если большинство задач требует интенсивной работы ЦП. Это не будет иметь большого значения для производительности на типичных системах - с такой большой перегрузкой будет плохо с 4 потоками и плохо с 400.

Как вы решили на 100 потоков? Почему не 16, скажем?

«Количество потоков не является массовым, скажем, до 100». - это меняется? Просто создайте 16 при запуске и перестаньте управлять ими - просто передайте им очередь и забудьте о них.

Ужасная мысль - вы не создаете новую ветку для каждой задачи?



 am I best off to just launch more threads when I need to do more work and let the Java thread scheduler handle distributing the work, or would getting smarter and managing the work load to keep the CPU below 100% get me further faster?

По мере добавления все большего количества потоков увеличиваются накладные расходы, связанные с переключением контекста, очисткой кэша памяти, переполнением кэша памяти и управлением потоками ядра и JVM. По мере того, как ваши потоки нагружают процессор своим кером, приоритеты nel снижаются до некоторого минимума, и они достигают минимума временных интервалов. По мере того, как все больше потоков заполняют память, они переполняют различные кеши внутренней памяти ЦП. Существует более высокая вероятность того, что ЦПУ потребуется поменять задание с более медленной памяти. Внутри JVM существует больше локальных конфликтов мьютекса и, возможно, некоторые (может быть, небольшие) дополнительные издержки GC для каждого потока и пропускной способности объекта. В зависимости от того, насколько синхронизированы ваши пользовательские задачи, большее количество потоков приведет к увеличению сброса памяти и конфликту блокировок.

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

Я бы порекомендовал использовать`Executors.newFixedThreadPool(SOME_NUMBER);` и представить вам работу к нему. Затем вы можете выполнить несколько запусков, изменяя количество потоков вверх и вниз, пока не найдете оптимальное количество одновременно работающих пулов в соответствии с работой и архитектурой блока.

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