Настройки памяти с тысячами потоков
Я играю с JVM (Oracle 1.7 64 бит) на Linux-боксе (AMD 6 Core, 16 ГБ ОЗУ), чтобы посмотреть, как количество потоков в приложении влияет на производительность. Я надеюсь измерить, в какой момент переключение контекста снижает производительность.
Я создал небольшое приложение, которое создает пул выполнения потоков:
Executors.newFixedThreadPool(numThreads)
Я настраиваюnumThreads
каждый раз, когда я запускаю программу, чтобы увидеть эффект, который она имеет.
Я тогда отправляюnumThread
рабочие места (случаиjava.util.concurrent.Callable
) в бассейн. Каждый увеличиваетAtomicInteger
, выполняет некоторую работу (создает массив случайных целых чисел и перемешивает его), а затем некоторое время спит. Идея состоит в том, чтобы смоделировать вызов веб-службы. Наконец, задание повторно отправляется в пул, так что у меня всегда естьnumThreads
рабочие места работают.
Я измеряю пропускную способность, как и количество заданий, которые обрабатываются в минуту.
С несколькими тысячами потоков я могу обрабатывать до 400 000 заданий в минуту. Выше 8000 потоков результаты начинают сильно различаться, что говорит о том, что переключение контекста становится проблемой. Но я могу продолжать увеличивать количество потоков до 30 000 и по-прежнему получать более высокую пропускную способность (от 420 000 до 570 000 заданий в минуту).
Теперь вопрос: я получаюjava.lang.OutOfMemoryError: Unable to create new native thread
с более чем около 31 000 рабочих мест. Я пробовал настройку-Xmx6000M
что не помогает Я пытался играть с-Xss
но это тоже не помогает.
Я прочитал этоulimit
может быть полезным, но с увеличениемulimit -u 64000
ничего не изменилось
Для информации:
[root@apollo ant]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 127557
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
Итак, вопрос № 1: Что мне нужно сделать, чтобы создать пул потоков большего размера?
Вопрос № 2: На каком этапе следует ожидать, что переключение контекста действительно уменьшит пропускную способность и приведет к остановке процесса?
Вот некоторые результаты после того, как я изменил его, чтобы сделать немного больше обработки (как было предложено) и начал записывать среднее время отклика (как было также предложено).
// ( (n_cores x t_request) / (t_request - t_wait) ) + 1
// 300 ms wait, 10ms work, roughly 310ms per job => ideal response time, 310ms
// ideal num threads = 1860 / 10 + 1 = 187 threads
//
// results:
//
// 100 => 19,000 thruput, 312ms response, cpu < 50%
// 150 => 28,500 thruput, 314ms response, cpu 50%
// 180 => 34,000 thruput, 318ms response, cpu 60%
// 190 => 35,800 thruput, 317ms response, cpu 65%
// 200 => 37,800 thruput, 319ms response, cpu 70%
// 230 => 42,900 thruput, 321ms response, cpu 80%
// 270 => 50,000 thruput, 324ms response, cpu 80%
// 350 => 64,000 thruput, 329ms response, cpu 90%
// 400 => 72,000 thruput, 335ms response, cpu >90%
// 500 => 87,500 thruput, 343ms response, cpu >95%
// 700 => 100,000 thruput, 430ms response, cpu >99%
// 1000 => 100,000 thruput, 600ms response, cpu >99%
// 2000 => 105,000 thruput, 1100ms response, cpu >99%
// 5000 => 131,000 thruput, 1600ms response, cpu >99%
// 10000 => 131,000 thruput, 2700ms response, cpu >99%, 16GB Virtual size
// 20000 => 140,000 thruput, 4000ms response, cpu >99%, 27GB Virtual size
// 30000 => 133,000 thruput, 2800ms response, cpu >99%, 37GB Virtual size
// 40000 => - thruput, -ms response, cpu >99%, >39GB Virtual size => java.lang.OutOfMemoryError: unable to create new native thread
Я интерпретирую их как:
1) Даже если приложение спит в течение 96,7% времени, все равно остается много переключений потоков. 2) Переключение контекста измеримо и отображается во времени отклика.
Здесь интересно то, что при настройке приложения вы можете выбрать приемлемое время ответа, скажем, 400 мс, и увеличивать количество потоков, пока не получите это время ответа, которое в этом случае позволит приложению обрабатывать около 95 тысяч запросов минута.
Часто люди говорят, что идеальное количество потоков близко к числу ядер. В приложениях, у которых есть время ожидания (заблокированные потоки, скажем, ожидание ответа базы данных или веб-службы), расчет должен учитывать это (см. Мое уравнение выше). Но даже этот теоретический идеал не является реальным идеалом, когда вы смотрите на результаты или когда настраиваетесь на конкретное время отклика.