O mul­ti­pro­ces­sa­mento Python permite dividir a carga de trabalho em vários processos e, assim, reduzir o tempo total de execução. Isso re­pre­senta uma grande vantagem, es­pe­ci­al­mente quando é ne­ces­sá­rio realizar muitos cálculos ou quando há grandes conjuntos de dados.

O que é mul­ti­pro­ces­sa­mento Python?

O mul­ti­pro­ces­sa­mento em Python refere-se à ca­pa­ci­dade de executar vários processos ao mesmo tempo para apro­vei­tar ao máximo o de­sem­pe­nho dos sistemas com múltiplos núcleos. Ao contrário dos sistemas de thread único, nos quais as tarefas são exe­cu­ta­das se­quen­ci­al­mente, o mul­ti­pro­ces­sa­mento permite que di­fe­ren­tes partes do programa sejam exe­cu­ta­das em paralelo e in­de­pen­den­te­mente. Cada processo tem o seu próprio espaço de memória e pode ser pro­ces­sado em núcleos de pro­ces­sa­dor separados, o que reduz sig­ni­fi­ca­ti­va­mente o tempo de execução para operações que exigem muitos cálculos ou têm re­qui­si­tos de tempo rigorosos.

O mul­ti­pro­ces­sa­mento Python pode ser utilizado no pro­ces­sa­mento e análise de dados, por exemplo, para processar grandes conjuntos de dados de forma mais rápida. Também pode ser usado em si­mu­la­ções e cálculos de modelagem, como em apli­ca­ções ci­en­tí­fi­cas, para reduzir os tempos de execução de cálculos complexos. Além disso, o mul­ti­pro­ces­sa­mento é usado na área de web scraping para coletar dados de várias páginas da web si­mul­ta­ne­a­mente, bem como no pro­ces­sa­mento de imagens e visão com­pu­ta­ci­o­nal para melhorar a efi­ci­ên­cia das operações de análise.

Onde é possível im­ple­men­tar o mul­ti­pro­ces­sa­mento em Python?

Python oferece várias pos­si­bi­li­da­des para im­ple­men­tar o mul­ti­pro­ces­sa­mento. A seguir, apre­sen­ta­mos três fer­ra­men­tas comuns: o módulo multiprocessing, a bi­bli­o­teca concurrent.futures e o pacote joblib.

O módulo multiprocessing

Multiprocessing é o módulo padrão para mul­ti­pro­ces­sa­mento em Python. Com ele, é possível criar processos, trocar dados entre esses processos e realizar sin­cro­ni­za­ções por meio de bloqueios, filas e outros me­ca­nis­mos.

import multiprocessing
def task(n):
    result = n * n
    print(f"Result: {result}")
if __name__ == "__main__":
    processes = []
    for i in range(1, 6):
        process = multiprocessing.Process(target=task, args=(i,))
        processes.append(process)
        process.start()
    for process in processes:
        process.join()
python

No exemplo anterior, a classe multiprocessing.Process foi usada para criar e iniciar processos que executam a função task(). Essa função pega o número trans­fe­rido e o eleva ao quadrado. Em seguida, cada processo é iniciado e aguarda-se que eles terminem antes de continuar com o programa principal. O resultado é obtido com uma f-string, que é um método do Python string format para ligar ex­pres­sões. A sequência de saída dos re­sul­ta­dos não segue uma ordem es­pe­cí­fica e pode variar em cada execução.

Você também pode criar um pool de processos com Python multiprocessing da seguinte maneira:

import multiprocessing
def task(n):
    return n * n
if __name__ == "__main__":
    with multiprocessing.Pool() as pool:
        results = pool.map(task, range(1, 6))
        print(results)  # Output: [1, 4, 9, 16, 25]
python

Com pool.map(), a função task() é aplicada a uma sequência de dados, e os re­sul­ta­dos são re­co­lhi­dos e de­vol­vi­dos.

A bi­bli­o­teca concurrent.futures

O módulo concurrent.futures fornece uma interface de alto nível para a execução as­sín­crona e o pro­ces­sa­mento paralelo de tarefas. Ele usa o Pool Executor para executar tarefas num pool de processos ou threads. Este módulo oferece uma maneira mais simples de lidar com tarefas as­sín­cro­nas e, em muitos casos, é mais fácil de usar do que o módulo mul­ti­pro­ces­sing do Python.

import concurrent.futures
def task(n):
    return n * n
with concurrent.futures.ProcessPoolExecutor() as executor:
    futures = [executor.submit(task, i) for i in range(1, 6)]
    for future in concurrent.futures.as_completed(futures):
        print(future.result()) # result in random order
python

O código utiliza o módulo concurrent.futures para processar tarefas em paralelo com o ProcessPoolExecutor. A função task(n) é trans­fe­rida para os números de 1 a 5. O método as_completed() aguarda a conclusão das tarefas e retorna os re­sul­ta­dos em ordem aleatória.

joblib

joblib é uma bi­bli­o­teca externa do Python concebida para sim­pli­fi­car o pro­ces­sa­mento em paralelo, por exemplo, para tarefas re­pe­ti­ti­vas como executar funções com di­fe­ren­tes pa­râ­me­tros de entrada ou trabalhar com grandes quan­ti­da­des de dados. As prin­ci­pais funções do joblib são pa­ra­le­li­zar tarefas, armazenar em cache os re­sul­ta­dos das funções e otimizar os recursos de memória e com­pu­ta­ção.

from joblib import Parallel, delayed
def task(n):
    return n * n
results = Parallel(n_jobs=4)(delayed(task)(i) for i in range(1, 11))
print(results) # Output: Results of the function for numbers from 1 to 10
python

Com a expressão Parallel(n_jobs=4)(delayed(task)(i) for i in range(1, 11)), inicia-se a execução em paralelo da função task() para os números de 1 a 10. Parallel está con­fi­gu­rado com o argumento n_jobs=4, o que indica que podem ser pro­ces­sa­dos até quatro trabalhos em paralelo. Ao chamar delayed(task)(i), cria-se a tarefa que deve ser executada em paralelo para cada número i no intervalo de 1 a 10. Ou seja, a função task() é chamada si­mul­ta­ne­a­mente para cada um desses números. O resultado para os números de 1 a 10 é ar­ma­ze­nado em results e impresso.

Ir para o menu principal