O problema que tive foi que Pool
usei todos os processos, mesmo tendo passado um inteiro no processes
argumento, Pool
que era muito menor que o número total de núcleos que o servidor tinha.
Eu uso pools de multiprocessamento para executar tarefas de longa duração. Por exemplo, executo o seguinte código simples.
n_cores = 20
with Pool(n_cores) as pool:
results = pool.map(example_function, args)
Isso é muito mais do que o número de núcleos que configurei, mas limitei o número de núcleos a 20 para usar quantos eu precisasse. Executei o código esperando que funcionasse bem, e ele concluiu a tarefa muito mais rápido do que usando um único núcleo. Recentemente, verifiquei o uso de núcleos por meio do htop
comando no Ubuntu e me deparei com uma realidade chocante. Todos os núcleos estão sendo usados enquanto a tarefa está em execução no pool, o que não era o que eu pretendia (usar 100% para todos os processos).
Gostaria de saber se existe uma solução alternativa para esse problema. Se não houver, precisamos considerar outros métodos, como o MPI.
Se estiver faltando alguma coisa, por favor me avise.
Obrigado.
Resposta curta: Não, em geral, um pool de multiprocessamento não força o uso de todos os núcleos.
Mas vamos esclarecer algumas coisas:
O primeiro argumento para o
Pool
inicializador, processes , é o número de processos a serem criados dentro do pool, que é totalmente diferente do número de núcleos a serem usados. Se você não especificar este argumento ou usar um valor deNone
, o número de processos que serão criados será o número de CPUs (núcleos) que você forneceu por ,multiprocessing.cpu_count()
e isso é diferente do número de CPUs utilizáveis, que é fornecido poros.process_cpu_count()
. Portanto, especificarn_cores = 20
e passarn_cores
para o inicializador do pool pode confundir alguém que não esteja familiarizado com o funcionamento do multiprocessamento. Esta variável faria mais sentido se fosse nomeadan_processes
.Então, quantos núcleos (CPUs) serão realmente utilizados? Supondo que você tenha N núcleos disponíveis, onde N < 20 (o tamanho do pool), temos vários casos:
example_function
for de longa duração e contiver atividade significativa de E/S ou de rede, de modo que o seu processo periodicamente cede o núcleo em que está sendo executado, permitindo que outros processos o utilizem até que a E/S seja concluída, é possível que todos os núcleos sejam usados — mas não necessariamente simultaneamente. Essa é uma situação em que pode ser útil criar um tamanho de pool maior que o número de núcleos, permitindo que mais atividades de E/S sejam sobrepostas.example_function
é de longa duração e 100% dependente da CPU (ou seja, sem E/S, atividade de rede, etc.). Nesse caso, eu esperaria que todos os núcleos fossem eventualmente usados pela sua função de trabalho se você estivesse enviando pelo menos N tarefas, mas isso não é garantido, dependendo de quais outros processos estão competindo por recursos da CPU. De qualquer forma, não faz sentido criar um tamanho de pool maior que N; você não pode ter mais de N computações limitadas à CPU em execução paralela quando você tem apenas N CPUs.example_function
tem execução extremamente curta e consome 100% da CPU (por exemplo, a função retorna apenas o argumento original passado a ela). Supondo que a duração deargs
(o número de tarefas sendo submetidas) seja relativamente pequena, é possível que, após a criação do primeiro processo do pool, ele consiga processar todas as tarefas submetidas antes que os demais processos do pool sejam criados. Nesse caso extremo, apenas uma CPU seria usada pela suamap
chamada.Dito isso, você afirma:
Qual é o seu problema? Você nunca deixa isso claro e, sim, acho que está faltando alguma coisa, e espero que a explicação acima esclareça.
Resumindo:
Se você tiver muitas tarefas de longa duração que combinam CPU e E/S, pode ser vantajoso criar um pool maior que o número de núcleos disponíveis, caso busque desempenho máximo . No entanto, não se preocupe com quais núcleos estão sendo usados para executar essas tarefas. Quando sua função de trabalho precisa da CPU para executar uma tarefa, o sistema operacional escolherá uma CPU para você, que pode ou não ser uma CPU previamente atribuída a qualquer uma de suas tarefas.
Se, no entanto, você quiser limitar os recursos que utiliza, faça isso criando um pool com menos processos. Por exemplo, você está enviando 100 tarefas, mas nunca quer que 4 tarefas sejam executadas em paralelo. Nesse caso, crie um pool com 4 processos. Mas, novamente, você não deve se importar com quais CPUs são atribuídas a esses 4 processos ao longo de sua vida útil, partindo do princípio de que uma CPU é tão boa quanto a outra.
Não se preocupe com o uso de 100% da CPU, pois seu sistema operacional gerencia todos os recursos. Fisicamente, ele não consegue usar 100%, provavelmente cerca de 99,(9)%. Se você realmente gosta da velocidade de execução do seu código, pode deixá-lo assim e não planeja executar nenhum outro programa em segundo plano. Caso contrário, recomendo reduzir pela metade.