Threads versus Processos. Os limites máximos.

Em ambiente linux os limites superiores da quantidade de processos e threads que podem ser disparados são configuráveis (diferente do que acontece com as “janelinhas”). Como já mostrei, threads podem ser consideradas como “processos leves”. A diferença é que as threads compartilha o espaço de endereçamento do processo, mas não compartilha a pilha!

Eis alguns limites:

$ 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) 15669
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) 15669
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

Note que não existe limite para threads, per se, nessa listagem. Existe, porém, para a quantidade de processos (quase 16000). Ou seja, minha máquina não é capaz, na configuração corrente, de criar mais que 15669 processos simultâneos. No entanto, embora o limite de threads não seja mostrado com ulimit, podemos obtê-lo no proc filesystem:

$ cat /proc/sys/kernel/threads-max
31339

Ou seja, no meu Linux, posso criar uma quantidade equivalente ao dobro do número de processos! Mas, tome cuidado… Esses valores são teóricos. Cada processo e cada thread alocam uma pilha. O tamanho default da pilha é o mostrado por ulimit (no meu caso, 8 MB). Para poder criar as 31339 threads eu devo ter, livres, quase 245 GB de memória só para as pilhas!

Uma maneira de chegar perto desse valor teórico (31339 threads) é óbvio. Diminuir o tamanho da pilha alocada para as threads secundárias (a thread “primária” é aquela criada para o processo, onde main será executado). A forma de fazer isso, usando a biblioteca pthreads é criar um atributo e indicar o tamanho da pilha desejada:

pthread_attr_t attr;
pthread_t tid;
void *myThreadFunc(void *); /* protótipo */

/* Inicializa e ajusta os atributos da nova thread */
pthread_attr_init(&attr);
pthread_attr_sizestacksize(&attr, 512*1024);

/* Cria e executa a thread */
pthread_create(&tid, &attr, myThreadFunc, NULL);
...
/* Espera a thread secundária terminar */
pthread_join(tid, NULL);

/* Livra-se dos atributos. */
pthread_attr_destroy(&attr);

Ignorei os testes de retorno para facilitar a leitura. No pseudocódigo acima ajustei o tamanho da thread que criarei com o tamanho mínimo de 512 kB, ao invés de 8 MB (o default, na minha máquina). É bom observar que as pilhas podem crescer o quanto puderem. O limite superior pode ser observado também com ulimit:

$ ulimit -H -a
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15669
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 4096
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15669
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

O parâmetro -H em ulimit nos dá o hard limit, ou seja, o máximo possível (se -H não for fornecido obtemos o soft limit, ou seja, o limite inferior). De novo, esses são valores teóricos e dependem de análise…

No início do texto eu disse que esses valores podem ser customizados. Podem… através de sysctl ou do proc filesystem. Mas, se você for alterá-los, faça o com muito cuidado. É bom saber o que está fazendo. Por exemplo, se você colocar um valor muito pequeno para o stack size, o Linux pode, simplesmente, não funcionar mais… Parece (ênfase no “parece”) que o limite inferior do sistema é de cerca de 128 kB.

O aviso acima não significa que você não possa ter valores menores para as threads secundárias (embora isso também “pareça” não ser uma boa idéia).

Para concluir, threads são mais interessantes do que processos, do ponto de vista de performance. Ainda temos a vantagem de poder criar mais threads do que processos forkados, bastando fazer alguns tweaks — usando pthreads attributes — para garantir que não vamos exaurir a memória física. Esses pequenos ajustes não são tão flexíveis assim quando falamos de processos…

Anúncios

2 comentários sobre “Threads versus Processos. Os limites máximos.

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s