Multiprocessamento em Python: Paralelismo Real

0 – Python
4 – Paralela e Concorrente
4.1 – Multithreading (threading)
4.2 – Multiprocessamento (multiprocessing)
4.3 – Assíncrona (asyncio)
4.4 – async ou await
4.4 – Distribuida
LEGENDA
Nivel_1
Nivel_2
Nivel_3

Multiprocessamento cria múltiplos processos independentes. Cada processo tem seu próprio interpretador Python e memória. Primeiramente, isso contorna a limitação do GIL completamente. Por exemplo, 4 processos podem executar em 4 núcleos de CPU simultaneamente. Além disso, processos não compartilham memória por padrão. A voz passiva é usada aqui: “dados são copiados entre processos via serialização”. Quando utilizar multiprocessing? Em tarefas com CPU intensiva. Por exemplo, processamento de imagens, cálculos matemáticos ou criptografia. Também é útil para explorar todo o potencial da máquina. Python oferece o módulo multiprocessing para isso. Vamos explorar criação, comunicação e boas práticas. Três subtítulos guiarão você pelo paralelismo real. Ao final, você dominará o multiprocessamento em Python.

Criando e gerenciando processos

O módulo multiprocessing tem interface similar ao threading. Use Process para criar processos individuais. O método start() inicia o processo e join() espera terminar. Para múltiplas tarefas, use Pool para gerenciar um conjunto de processos. Quando usar criação manual? Em poucos processos com lógica específica. A voz passiva é aplicada: “os argumentos são passados via args“. Exemplo básico de criação de processos:

Cada processo tem seu próprio PID (identificador único). O Pool gerencia automaticamente quantos processos rodam simultaneamente. Isso é mais eficiente que criar centenas de processos manuais.

Comunicação entre processos

Processos não compartilham memória como threads. Portanto, precisamos de mecanismos especiais para comunicação. O Queue permite trocar dados entre processos de forma segura. O Pipe oferece comunicação bidirecional entre dois processos. Já o Value e Array compartilham memória com locks. Quando usar cada um? Queue para produtor-consumidor. Pipe para comunicação simples entre dois processos. A voz passiva é aplicada: “os dados são serializados com pickle automaticamente”. Exemplo de comunicação com Queue:

Queues são ideais para padrões produtor-consumidor. Valores compartilhados exigem locks para evitar condições de corrida. A comunicação entre processos tem overhead, então use apenas quando necessário.

Pool e map para paralelismo de dados

A função pool.map() é a maneira mais fácil de paralelizar. Ela divide uma lista de dados entre os processos disponíveis. Cada processo aplica a mesma função a um subconjunto dos dados. Quando usar map? Em problemas de processamento de listas grandes. Por exemplo, aplicar uma função a cada elemento de 1 milhão de itens. A voz passiva é aplicada: “os resultados são coletados automaticamente”. Exemplo prático com processamento paralelo de números:

A aceleração deve ser próxima ao número de núcleos da CPU. Para 4 núcleos, espere cerca de 3.5x de ganho. A fórmula teórica é a lei de Amdahl: \(S = \frac{1}{(1 – P) + \frac{P}{N}}\) Onde P é a fração paralelizável e N é o número de núcleos. Multiprocessamento é a ferramenta certa para CPU-bound. Combine com boas práticas e evite overhead desnecessário. Seu código rodará muito mais rápido em máquinas modernas.

Deixe um comentário