Programação  
 
Rede de conhecimento computador >> Programação >> Programação Python >> Content
Como posso executar com eficiência um loop de python em paralelo?
Existem várias maneiras de executar com eficiência um loop de execução do Python em paralelo, dependendo do tipo de tarefa executada dentro do loop e dos recursos disponíveis. Aqui está um colapso de abordagens comuns e suas considerações:

1. Multiprocessamento (tarefas ligadas à CPU):

- Quando usar: Ideal para tarefas que são computacionalmente intensivas (ligadas à CPU), como trituração de números, processamento de imagens ou cálculos complexos. Essas tarefas se beneficiam mais da utilização de vários núcleos.

- como funciona: Cria processos separados, cada um com seu próprio espaço de memória. Isso evita as limitações globais de bloqueio de intérprete (GIL), permitindo a verdadeira execução paralela.

- Exemplo:

`` `Python
importar multiprocessamento
tempo de importação

DEF Process_item (item):
"" "" Simula uma tarefa ligada à CPU. "" "
time.sleep (1) # simular trabalho
Item de devolução * 2

def main ():
itens =lista (intervalo (10))
start_time =time.time ()

com multiprocessing.pool (processos =multiprocessing.cpu_count ()) como pool:
Resultados =pool.map (process_item, itens)

end_time =time.time ()
print (F "Resultados:{Results}")
print (f "TEMPO TEMPO:{end_time - start_time:.2f} segundos")

Se __name__ =="__main__":
principal()
`` `

- Explicação:

- `multiprocessing.pool`:cria um conjunto de processos de trabalhador. `multiprocessing.cpu_count ()` usa automaticamente o número de núcleos disponíveis.
- `pool.map`:aplica a função` process_item` a cada item na lista `itens`, distribuindo o trabalho pelos processos do trabalhador. Ele lida automaticamente dividindo o trabalho e a coleta dos resultados.
- `pool.apply_async`:uma alternativa não bloqueadora ao` pool.map`. Você precisa coletar resultados usando `resultado.get ()` para cada item.
- `pool.iMap` e` pool.iMap_unordered`:iteradores que retornam os resultados à medida que ficam disponíveis. `IMAP_UNORDED` não garante a ordem dos resultados.
- `pool.starmap`:semelhante ao` pool.map`, mas permite passar vários argumentos para a função do trabalhador usando tuplas.

- Vantagens:

- supera a limitação do GIL para tarefas ligadas à CPU.
- usa vários núcleos de CPU com eficiência.

- Desvantagens:

- Despesas gerais mais altas do que rosquear devido à criação de processos separados.
- A comunicação entre os processos (passando dados) pode ser mais lenta.
- mais intensiva em memória, pois cada processo tem seu próprio espaço de memória.
- pode ser mais complexo para gerenciar o estado compartilhado (precisa de mecanismos de comunicação entre processos, como filas ou memória compartilhada).

2. Threading (tarefas ligadas a E/O):

- Quando usar: Adequado para tarefas que gastam uma quantidade significativa de tempo aguardando operações externas (ligadas à E/S), como solicitações de rede, leituras/gravações em disco ou consultas de banco de dados.

- como funciona: Cria vários threads dentro de um único processo. Os tópicos compartilham o mesmo espaço de memória. O GIL (bloqueio global de intérprete) limita o paralelismo verdadeiro no cpython, mas os threads ainda podem melhorar o desempenho liberando o GIL ao aguardar a E/S.

- Exemplo:

`` `Python
importar rosqueamento
tempo de importação

def fetch_url (url):
"" "" Simula uma tarefa ligada a E/S. "" "
print (f "busca {url}")
time.sleep (2) # simule o atraso da rede
Imprima (F "FECTING FECTING {URL}")
Retorne F "Conteúdo de {url}"

def main ():
urls =["https://example.com/1", "https://example.com/2", "https://example.com/3"]
start_time =time.time ()

threads =[]
Resultados =[]
Para URL em URLs:
Thread =Threading.Thread (Target =Lambda u:Results.append (fetch_url (u)), args =(url,))
threads.append (thread)
Thread.start ()

Para threads em threads:
Thread.join () # Aguarde todos os threads concluírem

end_time =time.time ()
print (F "Resultados:{Results}")
print (f "TEMPO TEMPO:{end_time - start_time:.2f} segundos")

Se __name__ =="__main__":
principal()
`` `

- Explicação:

- `Threading.Thread`:cria um novo thread.
- `thread.start ()`:inicia a execução do thread.
- `thread.join ()`:aguarda o thread terminar.
- Função lambda: Usado para passar o `url` como um argumento para` fetch_url` dentro do construtor `thread`. É essencial passar o `url` * por valor * para evitar condições de corrida, onde todos os threads podem acabar usando o último valor de` url '.

- Vantagens:

- Despesas inferiores do que multiprocessamento.
- compartilha o espaço da memória, facilitando o compartilhamento de dados entre os threads (mas requer sincronização cuidadosa).
- Pode melhorar o desempenho para tarefas ligadas a E/S, apesar do GIL.

- Desvantagens:

- O GIL limita o paralelismo verdadeiro para tarefas ligadas à CPU em Cpython.
- Requer sincronização cuidadosa (bloqueios, semáforos) para evitar condições de corrida e corrupção de dados ao acessar recursos compartilhados.

3. Asyncio (concorrência com um único thread):

- Quando usar: Excelente para lidar com um grande número de tarefas ligadas a E/S simultaneamente dentro de um único thread. Fornece uma maneira de escrever um código assíncrono que pode alternar entre tarefas enquanto aguarda a conclusão das operações de E/S.

- como funciona: Usa um loop de eventos para gerenciar coroutinas (funções especiais declaradas com `assíncrona def`). As coroutinas podem suspender sua execução enquanto aguardam a E/S e permitem que outras coroutinas funcionem. `Asyncio` não * não fornece paralelismo verdadeiro (é simultaneamente), mas pode ser altamente eficiente para operações ligadas a E/S.

- Exemplo:

`` `Python
importar asyncio
tempo de importação

Async def fetch_url (url):
"" "" Simula uma tarefa ligada a E/S (assíncrona). "" "
print (f "busca {url}")
Aguarda asyncio.sleep (2) # simular o atraso da rede (não bloqueio)
Imprima (F "FECTING FECTING {URL}")
Retorne F "Conteúdo de {url}"

Async def main ():
urls =["https://example.com/1", "https://example.com/2", "https://example.com/3"]
start_time =time.time ()

Tarefas =[Fetch_url (URL) para URL em URLs]
Resultados =Aguarda asyncio.gather (*tarefas) # Execute tarefas simultaneamente

end_time =time.time ()
print (F "Resultados:{Results}")
print (f "TEMPO TEMPO:{end_time - start_time:.2f} segundos")

Se __name__ =="__main__":
asyncio.run (main ())
`` `

- Explicação:

- `assíncrono def`:define uma função assíncrona (coroutina).
- `Aguarda`:suspende a execução da coroutina até que a operação aguardada seja concluída. Ele libera controle para o loop do evento, permitindo que outras coroutinas sejam executadas.
- `syncio.sleep`:uma versão assíncrona do` time.sleep` que não bloqueia o loop do evento.
- `Asyncio.gather`:executa várias coroutinas simultaneamente e retorna uma lista de seus resultados na ordem em que foram enviados. `*Tasks` descompacta a lista de tarefas.
- `Asyncio.run`:inicia o loop de eventos Asyncio e executa a coroutina` main`.

- Vantagens:

- altamente eficiente para tarefas ligadas a E/S, mesmo com um único thread.
- Evita a sobrecarga de criar vários processos ou threads.
- Mais fácil de gerenciar a simultaneidade do que a rosqueamento (menos necessidade de bloqueios explícitos).
- Excelente para criar aplicativos de rede altamente escaláveis.

- Desvantagens:

- Requer o uso de bibliotecas e código assíncronos, que podem ser mais complexos para aprender e depurar do que o código síncrono.
- Não é adequado para tarefas ligadas à CPU (não fornece paralelismo verdadeiro).
- depende de bibliotecas compatíveis com assíncronas (por exemplo, `aiohttp` em vez de 'solicitações').

4. Concurrent.futures (abstração sobre multiprocessamento e rosqueamento):

- Quando usar: Fornece uma interface de alto nível para executar tarefas de forma assíncrona, usando threads ou processos. Permite alternar entre rosqueamento e multiprocessamento sem alterar significativamente seu código.

- como funciona: Usa o `threadpoolExecutor` para encadeamento e` processpoolExecutor` para multiprocessamento.

- Exemplo:

`` `Python
importação concorrente.futures
tempo de importação

DEF Process_item (item):
"" "" Simula uma tarefa ligada à CPU. "" "
time.sleep (1) # simular trabalho
Item de devolução * 2

def main ():
itens =lista (intervalo (10))
start_time =time.time ()

com concurrent.futures.processpoolExecutor (max_workers =multiprocessing.cpu_count ()) como executor:
# Envie cada item ao executor
futuros =[Executor.submit (process_item, item) para item em itens]

# Espere que todos os futuros concluam e obtenha os resultados
Resultados =[FUTURO.RESULT () para o futuro em concorrente.futures.as_completed (Futures)]

end_time =time.time ()
print (F "Resultados:{Results}")
print (f "TEMPO TEMPO:{end_time - start_time:.2f} segundos")

Se __name__ =="__main__":
importar multiprocessamento
principal()
`` `

- Explicação:

- `Concurrent.futures.processpoolExecutor`:cria um conjunto de processos de trabalhadores. Você também pode usar `concorrente.futures.threadpoolExecutor` para threads.
- `Executor.subMit`:envia um chamável (função) ao executor para execução assíncrona. Retorna um objeto `futuro` representando o resultado da execução.
- `Concurrent.futures.as_completed`:um iterador que produz objetos` futuro` à medida que eles concluem, em nenhuma ordem específica.
- `futuro.Result ()`:Recupera o resultado da computação assíncrona. Ele bloqueará até que o resultado esteja disponível.

- Vantagens:

- Interface de alto nível, simplificando a programação assíncrona.
- Alterne facilmente entre encadeamentos e processos alterando o tipo de executor.
- Fornece uma maneira conveniente de gerenciar tarefas assíncronas e recuperar seus resultados.

- Desvantagens:

- pode ter um pouco mais de sobrecarga do que usar `multiprocessing` ou` shreading` diretamente.

Escolhendo a abordagem correta:

| Abordagem | Tipo de tarefa | Limitação de Gil | Uso da memória | Complexidade |
| --------------------- | ----------------- | ---------------- | --------------- | ------------ |
| Multiprocessamento | Ligado à CPU | Superar | Alto | Moderado |
| Threading | E/O-Bound | Sim | Baixo | Moderado |
| ASYNCIO | E/O-Bound | Sim | Baixo | Alto |
| Concorrente.futures | Ambos | Depende | Varia | Baixo |

Considerações importantes:

* Tipo de tarefa (ligado à CPU vs. ligado a E/O): Este é o fator mais importante. As tarefas ligadas à CPU se beneficiam do multiprocessamento, enquanto as tarefas ligadas a E/O são mais adequadas para rosquear ou assíncel.

* gil (bloqueio global de intérpretes): O GIL em Cpython limita o paralelismo verdadeiro no encadeamento. Se você precisar de um paralelismo verdadeiro para tarefas ligadas à CPU, use multiprocessamento.

* Sobrecarga: O multiprocessamento tem uma sobrecarga mais alta que a rosqueamento e o Asyncio.

* Uso da memória: O multiprocessamento usa mais memória porque cada processo tem seu próprio espaço de memória.

* Complexidade: Asyncio pode ser mais complexo para aprender do que rosquear ou multiprocessamento.

* compartilhamento de dados: O compartilhamento de dados entre processos (multiprocessamento) requer mecanismos de comunicação entre processos (filas, memória compartilhada), o que pode adicionar complexidade. Os threads compartilham o espaço da memória, mas requerem sincronização cuidadosa para evitar condições de corrida.

* Suporte da biblioteca: Certifique -se de que as bibliotecas que você está usando sejam compatíveis com o Asyncio se você escolher essa abordagem. Muitas bibliotecas agora oferecem versões assíncronas (por exemplo, `aiohttp` para solicitações HTTP).

Melhores práticas:

* Profile seu código: Antes de implementar o paralelismo, perfure seu código para identificar os gargalos. Não otimize prematuramente.

* Meça o desempenho: Teste diferentes abordagens e meça seu desempenho para determinar qual deles funciona melhor para o seu caso de uso específico.

* Mantenha as tarefas independentes: Quanto mais independentes são suas tarefas, mais fácil será paralelizá -las.

* manuseio exceções: Lidar adequadamente com exceções nas funções ou coroutinas do seu trabalhador para impedir que elas colidam com todo o aplicativo.

* Use filas para comunicação: Se você precisar se comunicar entre processos ou threads, use filas para evitar condições de corrida e garantir a segurança da linha.

* Considere uma fila de mensagem: Para sistemas complexos e distribuídos, considere usar uma fila de mensagens (por exemplo, RabbitMQ, Kafka) para processamento de tarefas assíncronas.

Ao considerar cuidadosamente esses fatores, você pode escolher a abordagem mais eficiente para executar o loop de execução do Python em paralelo e melhorar significativamente o desempenho do seu aplicativo. Lembre -se de testar e medir os resultados para garantir que sua abordagem escolhida esteja realmente fornecendo um benefício de desempenho.

Anterior :

Próximo :
  Os artigos relacionados
·Como o log de execução Tempo de teste em Python 
·Como remover dados de uma matriz em Python 
·Como Fazer um EXE de um arquivo Python 
·Como fazer nomes globais em Python 
·Como copiar Bytes em Python 
·Como substituir uma função em Python 
·Listas em Python 
·Como desenhar uma linha reta em Python Turtle 
·Como fazer uma string em uma URL no Django 
·Como aprender Python Linguagem Informática 
  Artigos em destaque
·Como criar um buffer em Python 
·Como usar a variável numérica em Python Script 
·Como formatar uma String Nome apropriado em VB 
·Como usar números negativos em C 
·Como converter um Datestamp 
·Como criar classes de exceção em Java 
·O que estão fluindo Classes 
·Como corrigir a biblioteca C + + Visual Runtime 
·Como corrigir Microsoft Visual C Debug Biblioteca 
·Como enviar um fax com PHP 
Cop e direita © Rede de conhecimento computador https://ptcomputador.com Todos os Direitos Reservados