Configurações de memória com milhares de segmentos

Estou brincando com a JVM (Oracle 1.7 64 bit) em uma caixa do Linux (AMD 6 Core, 16 GB de RAM) para ver como o número de threads em um aplicativo afeta o desempenho. Espero medir em que ponto a alternância de contexto diminui o desempenho.

Eu criei um pequeno aplicativo que cria um pool de execução de thread:

Executors.newFixedThreadPool(numThreads)

Eu ajustonumThreads toda vez que eu corro o programa, para ver o efeito que tem.

Eu então envionumThread empregos (instâncias dejava.util.concurrent.Callable) para a piscina. Cada um incrementa umAtomicInteger, faz algum trabalho (cria uma matriz de números inteiros aleatórios e embaralha-o) e, em seguida, dorme um pouco. A ideia é simular uma chamada de serviço da web. Finalmente, o trabalho reapresenta-se ao pool, para que eu sempre tenhanumThreads empregos trabalhando.

Estou medindo o rendimento, como no número de trabalhos processados ​​por minuto.

Com vários milhares de threads, posso processar até 400.000 trabalhos por minuto. Acima de 8000 threads, os resultados começam a variar muito, sugerindo que a mudança de contexto está se tornando um problema. Mas posso continuar aumentando o número de threads para 30.000 e ainda obter um throughput mais alto (entre 420.000 e 570.000 jobs por minuto).

Agora a pergunta: eu recebo umjava.lang.OutOfMemoryError: Unable to create new native thread com mais de 31.000 empregos. Eu tentei definir-Xmx6000M o que não ajuda. Eu tentei brincar com-Xss mas isso também não ajuda.

Eu li issoulimit pode ser útil, mas aumentando comulimit -u 64000 não mudou nada.

Para informações:

[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

Então, a questão # 1: O que eu tenho que fazer para criar um pool de threads maior?

Pergunta # 2: Em que estágio eu esperaria ver a troca de contexto realmente reduzindo o rendimento e fazendo com que o processo parasse?

Aqui estão alguns resultados, depois que eu o modifiquei para fazer um pouco mais de processamento (como foi sugerido) e comecei a registrar tempos médios de resposta (como também foi sugerido).

// ( (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

Eu interpreto-os como:

1) Mesmo que o aplicativo esteja em repouso por 96,7% das vezes, isso ainda deixa muita troca de thread a ser feita 2) A comutação de contexto é mensurável e é mostrada no tempo de resposta.

O que é interessante aqui é que Ao ajustar um aplicativo, você poderia escolher o tempo de resposta aceitável, digamos 400 ms, e aumentar o número de segmentos até obter esse tempo de resposta, que nesse caso permitiria que o aplicativo processasse cerca de 95 mil solicitações minuto.

Muitas vezes as pessoas dizem que o número ideal de threads está próximo do número de núcleos. Em aplicativos que têm tempo de espera (encadeamentos bloqueados, digamos, esperando por um banco de dados ou serviço da Web para responder), o cálculo precisa considerar isso (veja minha equação acima). Mas mesmo esse ideal teórico não é um ideal real, quando você olha para os resultados ou quando sintoniza um tempo de resposta específico.