Paralelando um loop `para` em Python para melhorar o desempenho envolve a distribuição das iterações do loop em vários núcleos de processador. Existem várias abordagens, cada uma com seus próprios pontos fortes e fracos:
1. Usando `multiprocessing`: Essa é geralmente a melhor abordagem para tarefas ligadas à CPU (tarefas que gastam a maior parte do tempo fazendo cálculos). Ele cria vários processos, cada um executando uma parte do loop.
`` `Python
importar multiprocessamento
DEF Process_item (item):
"" "A função a ser aplicada a cada item no loop." ""
# Seu código para processar um único item vai aqui
Result =Item * 2 # Exemplo:Doble o item
resultado de retorno
Se __name__ =='__main__':# importante para a compatibilidade do Windows
itens =lista (intervalo (1000)) # Exemplo de lista de itens
com multiprocessing.pool (processos =multiprocessing.cpu_count ()) como pool:
Resultados =pool.map (process_item, itens)
Imprimir (resultados)
`` `
*
`multiprocessing.pool`: Cria um conjunto de processos de trabalhador. `multiprocessing.cpu_count ()` determina o número ideal de processos com base nos núcleos do seu sistema. Você pode ajustar esse número, se necessário.
*
`pool.map`: Aplica a função `process_item` a cada item nos` itens` iterable. Ele lida com a distribuição do trabalho e a coleta dos resultados com eficiência.
*
`se __name__ =='__main __':`: Isso é crucial, especialmente nas janelas, para impedir a criação de vários processos recursivamente.
2. Usando `concorrente.futures`: Fornece uma interface de nível superior ao multiprocessamento e rosqueamento, oferecendo mais flexibilidade.
`` `Python
importação concorrente.futures
DEF Process_item (item):
"" "A função a ser aplicada a cada item no loop." ""
# Seu código para processar um único item vai aqui
Result =Item * 2 # Exemplo:Doble o item
resultado de retorno
se __name__ =='__main__':
Itens =Lista (intervalo (1000))
com concurrent.futures.processpoolExecutor () como executor:
Resultados =List (Execoror.map (process_item, itens))
Imprimir (resultados)
`` `
Isso é muito semelhante ao `multiprocessamento ', mas muitas vezes considerado mais pitônico e mais fácil de usar. `ProcessPoolExecutor` usa processos, enquanto` threadpoolExecutor` usa threads (melhor para tarefas ligadas a E/O).
3. Usando `threading` (para tarefas ligadas a E/O): Se o seu loop envolver muita espera (por exemplo, solicitações de rede, E/S de arquivo), os threads podem ser mais eficientes que os processos. No entanto, o bloqueio global de intérprete (GIL) no CPython limita o paralelismo verdadeiro para tarefas ligadas à CPU nos threads.
`` `Python
importar rosqueamento
DEF Process_item (item, resultados):
"" "A função a ser aplicada a cada item no loop." ""
# Seu código para processar um único item vai aqui
Result =Item * 2 # Exemplo:Doble o item
Results.Append (resultado)
se __name__ =='__main__':
Itens =Lista (intervalo (1000))
Resultados =[]
threads =[]
Para itens em itens:
Thread =Threading.Thread (Target =process_item, args =(item, resultados))
threads.append (thread)
Thread.start ()
Para threads em threads:
Thread.Join ()
Imprimir (resultados)
`` `
Este exemplo é mais complexo porque você precisa gerenciar threads e uma lista de resultados compartilhados explicitamente. `concurrent.futures.threadpoolExecutor` simplifica isso significativamente.
Escolhendo o método certo: *
ligado à CPU: Use `multiprocessing` ou` concurrent.futures.processpoolExecutor '. Os processos ignoram o GIL e permitem o verdadeiro paralelismo.
*
i/o-ligado: Use `concorrente.futures.threadpoolExecutor`. Os threads são mais leves que os processos, e a sobrecarga da comutação de contexto é menor. Se a E/S for muito lenta, isso pode melhorar o desempenho mesmo com o GIL.
*
misto: Se o seu loop tiver peças ligadas à CPU e à I/S, você pode precisar de uma abordagem mais sofisticada, potencialmente combinando threads e processos ou usando programação assíncrona (por exemplo, `asyncio`).
Considerações importantes: *
Sobrecarga: Criar e gerenciar processos ou threads introduz a sobrecarga. A paralelização fornece apenas um benefício se o trabalho realizado por item for substancial o suficiente para superar essa sobrecarga.
*
compartilhamento de dados: O compartilhamento de dados entre processos é mais complexo do que compartilhar dados entre threads. Considere o uso de filas ou outros mecanismos de comunicação entre processos, se necessário.
*
Depuração: Debugar o código paralelo pode ser um desafio. Comece com pequenos exemplos e aumenta gradualmente a complexidade.
Lembre -se de perfilar seu código para medir a melhoria do desempenho após a paralelização. É possível que a paralelização não forneça um benefício significativo ou até diminua as coisas, se a sobrecarga for muito alta ou a tarefa não for adequada para a paralelização.