Mohamed Biaz

Mohamed Biaz

¿Por qué las colas de multiprocesamiento son lentas al compartir objetos grandes en Python?

gafas delante de un ordenador mostrando código

Las colas de multiprocesamiento en Python permiten que múltiples procesos intercambien objetos de forma segura entre sí. Sin embargo, estas colas pueden volverse lentas cuando se comparten objetos grandes entre procesos. Hay varias razones por las que esto puede ocurrir:

  1. Pickling y unpickling: Los objetos colocados en las colas de multiprocesamiento se someten a pickling, se transfieren a través de la cola y luego se someten a unpickling. Los pasos de pickling y unpickling añaden una sobrecarga, y para objetos grandes esta sobrecarga puede ser significativa. Esto se debe a que los objetos grandes requieren más datos para ser sometidos a pickling y transferidos, y el paso de unpickling requiere reconstruir el objeto completo.
  2. Limitaciones del GIL: El Global Interpreter Lock (GIL) impide que múltiples hilos nativos ejecuten bytecodes de Python simultáneamente. Este bloqueo es necesario principalmente porque la gestión de memoria de Python no es segura para hilos. Dado que las colas de multiprocesamiento utilizan bloqueos para transferir objetos de forma segura entre procesos, el GIL puede limitar el rendimiento al transferir objetos grandes que requieren adquirir el bloqueo durante largos períodos de tiempo.
  3. Copia de memoria: Las colas de multiprocesamiento crean copias de objetos cuando se transfieren entre procesos. Para objetos grandes, la creación de copias puede ser muy costosa en términos de tiempo y memoria.

Para mejorar el rendimiento, es mejor evitar compartir objetos grandes a través de colas de multiprocesamiento cuando sea posible.

Si es necesario, un enfoque es compartir objetos en memoria utilizando el multiprocessing.sharedctypes módulo en lugar de pickling, lo que evita la sobrecarga de pickling y unpickling.

Experimento de multiprocesamiento que destaca el problema

Aquí está el experimento en Python. La única diferencia entre nuestros dos experimentos es el tamaño de los objetos compartidos a través de la cola. Todo el cálculo intensivo (la multiplicación de matrices) es exactamente el mismo, independientemente de lo que pongamos en la cola.

from tqdm import tqdm
import multiprocessing as mp
from time import time
import numpy as np

def heavy_function(n):
for _ in range(n):
# some computation
a = np.random.random((500, 500))
b = np.random.random((500, 500))
_ = a.dot(b)
q.put(1)

if __name__ == "__main__":
num_workers = 16
n = 100
q = mp.Queue(maxsize=100)

t0 = time()
processes = []
for _ in range(num_workers):
p = mp.Process(target=heavy_function, args=(n,))
p.start()
processes.append(p)

for i in tqdm(range(num_workers)):
el = q.get(block=True, timeout=10)
for p in processes:
assert not p.is_alive()
p.join()
t1 = time()
print(f"{(t1 - t0) :.1f} seconds.")

Esto se ejecuta en mi ordenador en aproximadamente 7 segundos. Como podemos ver, el estado de cada subproceso es R, lo que significa que está en ejecución.

Multiprocessing python htop

En su lugar, pongamos un array numpy pesado en la cola.

Reemplace la línea donde ponemos un 1 en la cola por :

q.put(np.zeros((1500, 1500, 3))

Esto se ejecuta en mi ordenador en aproximadamente 145 segundos. Es más de 20 veces más lento, incluso si los cálculos son exactamente los mismos. Como podemos ver, el estado de cada subproceso es S, lo que significa que está en reposo.

Multiprocessing python htop

Escribir directamente en el disco duro para comunicarse entre procesos en lugar de usar colas de multiprocesamiento en Python tiene algunas ventajas y desventajas:

Ventajas:

  • Evitar la sobrecarga de pickling y unpickling para objetos grandes. Escribir en el disco evita serializar los objetos.
  • Evitar la copia de memoria que ocurre con las colas. Los objetos se escriben una vez en el disco en lugar de ser copiados a la cola.
  • Rendimiento de E/S potencialmente más rápido al escribir secuencialmente en el disco en lugar de añadir a una cola.

Desventajas:

  • La sincronización entre procesos debe manejarse manualmente. Las colas proporcionan una implementación segura para hilos (thread-safe) para intercambiar objetos entre procesos.
  • El manejo de errores es más complejo. La implementación de la cola maneja los errores que pueden ocurrir durante las transferencias de objetos. Esta lógica necesitaría ser reimplementada.
  • Los objetos en disco no son accesibles desde Python y deben cargarse antes de su uso. Los objetos de la cola permanecen en memoria.

Acerca de

Desde fotos sencillas hasta PDF complejos o archivos manuscritos, la API de Mindee convierte los datos de tus documentos en JSON estructurado con alta fiabilidad. No se requiere entrenamiento de modelos. Compatible con cualquier alfabeto y cualquier idioma.

,
,

Punto clave

Punto clave