Python mu­l­ti­pro­ce­s­si­ng te permite dividir la carga de trabajo en varios procesos y reducir así el tiempo total de ejecución. Esto supone una gran ventaja, es­pe­cia­l­me­n­te cuando deben rea­li­zar­se muchos cálculos o cuando hay grandes conjuntos de datos.

¿Qué es Python mu­l­ti­pro­ce­s­si­ng?

El mu­l­ti­pro­ce­sa­mie­n­to en Python se refiere a la capacidad de ejecutar varios procesos a la vez para apro­ve­char al máximo el re­n­di­mie­n­to de los sistemas de núcleo múltiple. A di­fe­re­n­cia de los sistemas de un solo hilo, en los que las tareas se ejecutan de forma se­cue­n­cial, el mu­l­ti­pro­ce­sa­mie­n­to permite que di­fe­re­n­tes partes del programa se ejecuten en paralelo e in­de­pe­n­die­n­te­me­n­te. Cada proceso tiene su propio espacio de memoria y se puede procesar en núcleos de pro­ce­sa­dor separados, lo que reduce si­g­ni­fi­ca­ti­va­me­n­te el tiempo de ejecución para ope­ra­cio­nes que requieren muchos cálculos o tienen re­qui­si­tos de tiempo estrictos.

El Python mu­l­ti­pro­ce­s­si­ng puede uti­li­zar­se en el pro­ce­sa­mie­n­to y análisis de datos, por ejemplo, para procesar grandes conjuntos de datos de manera más rápida. También puede emplearse en si­mu­la­cio­nes y cálculos de modelado, como en apli­ca­cio­nes cie­n­tí­fi­cas, para reducir los tiempos de ejecución de cálculos complejos. Además, el mu­l­ti­pro­ce­sa­mie­n­to se utiliza en el ámbito del web scraping para recopilar datos de varias páginas web si­mu­l­tá­nea­me­n­te, así como en el pro­ce­sa­mie­n­to de imágenes y visión por co­mpu­tado­ra para mejorar la efi­cie­n­cia de las ope­ra­cio­nes de análisis.

¿Dónde se puede im­ple­me­n­tar el mu­l­ti­pro­ce­sa­mie­n­to en Python?

Python ofrece varias po­si­bi­li­da­des de im­ple­me­n­tar el mu­l­ti­pro­ce­sa­mie­n­to. A co­n­ti­nua­ción, te pre­se­n­ta­mos tres he­rra­mie­n­tas ha­bi­tua­les: el módulo multiprocessing, la bi­blio­te­ca concurrent.futures y el paquete joblib.

El módulo multiprocessing

Multiprocessing es el módulo estándar para el mu­l­ti­pro­ce­sa­mie­n­to de Python. Con él es posible crear procesos, in­te­r­ca­m­biar datos entre esos procesos y realizar si­n­cro­ni­za­cio­nes mediante bloqueos, colas y otros me­ca­ni­s­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

En el anterior ejemplo se ha utilizado la clase multiprocessing.Process para crear e iniciar procesos que ejecutan la función task(). Esta función toma el número tra­n­s­fe­ri­do y lo eleva al cuadrado. Después, se inicia cada proceso y se espera a que terminen antes de continuar con el programa principal. El resultado se obtiene con un f-string, que es un método de Python string format para enlazar ex­pre­sio­nes. La secuencia de salida de los re­su­l­ta­dos no sigue un orden es­pe­cí­fi­co y puede variar en cada ejecución.

También puedes crear un pool de procesos con Python multiprocessing de la siguiente manera:

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

Con pool.map() se aplica la función task() a una secuencia de datos, y se recopilan y devuelven los re­su­l­ta­dos.

La bi­blio­te­ca concurrent.futures

El módulo concurrent.futures pro­po­r­cio­na una interfaz de alto nivel para la ejecución asíncrona y el pro­ce­sa­mie­n­to en paralelo de tareas. Utiliza el Pool Executor para ejecutar tareas en un pool de procesos o hilos. Este módulo ofrece una manera más sencilla de manejar tareas así­n­cro­nas y, en muchos casos, es más fácil de usar que el módulo mu­l­ti­pro­ce­s­si­ng de 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

El código utiliza el módulo concurrent.futures para procesar tareas en paralelo con el ProcessPoolExecutor. La función task(n) se tra­n­s­fie­re para números del 1 al 5. El método as_completed() espera a que se completen las tareas y devuelve los re­su­l­ta­dos en un orden aleatorio.

joblib

joblib es una bi­blio­te­ca externa de Python diseñada para si­m­pli­fi­car el pro­ce­sa­mie­n­to en paralelo, por ejemplo, para tareas re­pe­ti­ti­vas como ejecutar funciones con di­fe­re­n­tes pa­rá­me­tros de entrada o trabajar con grandes ca­n­ti­da­des de datos. Las pri­n­ci­pa­les funciones de joblib son pa­ra­le­li­zar tareas, almacenar en caché los re­su­l­ta­dos de las funciones y optimizar los recursos de memoria y co­mpu­tación.

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

Con la expresión Parallel(n_jobs=4)(delayed(task)(i) for i in range(1, 11)) se inicia la ejecución en paralelo de la función task() para los números del 1 al 10. Parallel está co­n­fi­gu­ra­do con el argumento n_jobs=4, lo que indica que se pueden procesar hasta cuatro trabajos en paralelo. Al llamar a delayed(task)(i), se crea la tarea que debe eje­cu­tar­se en paralelo para cada número i en el rango del 1 al 10. Es decir, se llama a la función task() si­mu­l­tá­nea­me­n­te para cada uno de esos números. El resultado para los números del 1 al 10 se almacena en results y se imprime.

Ir al menú principal