¿Chunksize es irrelevante para multiprocesamiento / pool.map en Python?

Intento utilizar la funcionalidad de multiprocesamiento de pool de python.

Independientemente de cómo establezca el tamaño del fragmento (en Windows 7 y Ubuntu, este último se ve a continuación con 4 núcleos), la cantidad de hilos paralelos parece permanecer igual.

from multiprocessing import Pool from multiprocessing import cpu_count import multiprocessing import time def f(x): print("ready to sleep", x, multiprocessing.current_process()) time.sleep(20) print("slept with:", x, multiprocessing.current_process()) if __name__ == '__main__': processes = cpu_count() print('-' * 20) print('Utilizing %d cores' % processes) print('-' * 20) pool = Pool(processes) myList = [] runner = 0 while runner < 40: myList.append(runner) runner += 1 print("len(myList):", len(myList)) # chunksize = int(len(myList) / processes) # chunksize = processes chunksize = 1 print("chunksize:", chunksize) pool.map(f, myList, 1) 

El comportamiento es el mismo si utilizo chunksize = int(len(myList) / processes) , chunksize = processes o 1 (como en el ejemplo anterior).

¿Podría ser que el tamaño de los trozos se ajusta automáticamente a la cantidad de núcleos?

Ejemplo para chunksize = 1 :

 -------------------- Utilizing 4 cores -------------------- len(myList): 40 chunksize: 10 ready to sleep 0  ready to sleep 1  ready to sleep 2  ready to sleep 3  slept with: 0  ready to sleep 4  slept with: 1  ready to sleep 5  slept with: 2  ready to sleep 6  slept with: 3  ready to sleep 7  slept with: 4  ready to sleep 8  slept with: 5  ready to sleep 9  slept with: 6  ready to sleep 10  slept with: 7  ready to sleep 11  slept with: 8  

Chunksize no influye en la cantidad de núcleos que se utilizan, esto se establece mediante el parámetro de processes de Pool . Chunksize establece cuántos elementos del iterable que pasa a Pool.map , se distribuyen a la vez por cada proceso de trabajo en lo que Pool llama una “tarea” (la figura a continuación muestra Python 3.7.1).

task_python_3.7.1

En el caso de que establezca chunksize=1 , un proceso de trabajo se alimenta con un nuevo elemento, en una nueva tarea, solo después de finalizar el recibido anteriormente. Para chunksize > 1 un trabajador obtiene un lote completo de elementos a la vez dentro de una tarea y cuando termina, obtiene el siguiente lote si queda alguno.

La distribución de elementos uno por uno con chunksize=1 aumenta la flexibilidad de la progtwigción, al tiempo que reduce el rendimiento general, ya que la alimentación por goteo requiere más comunicación entre procesos (IPC).

En mi análisis en profundidad del algoritmo de tamaño de Pool aquí , defino la unidad de trabajo para procesar un elemento del iterable como taskel , para evitar conflictos de nombres con el uso de Pool de la palabra “tarea”. Una tarea (como unidad de trabajo) consiste en chunksize de chunksize reducido.

chunksize=1 establecer chunksize=1 si no puede predecir cuánto tiempo necesitará terminar una tarea, por ejemplo, un problema de optimización, donde el tiempo de procesamiento varía mucho entre las tareas. La alimentación por goteo aquí evita que el proceso de un trabajador se asiente sobre una stack de artículos intactos, mientras se choca en una tarea pesada, lo que impide que los otros elementos de su tarea se distribuyan a los procesos de trabajo inactivos.

De lo contrario, si todas las tareas necesitarán el mismo tiempo para finalizar, puede configurar los chunksize=len(iterable) // processes , de modo que las tareas solo se distribuyan una vez entre todos los trabajadores. Tenga en cuenta que esto producirá una tarea más que los procesos (procesos + 1) en caso de que len(iterable) / processes tenga un rest. Esto tiene el potencial de impactar severamente el tiempo total de cómputo. Lea más sobre esto en la respuesta previamente vinculada.


Para su información, esa es la parte del código fuente donde Pool calcula internamente el tamaño del trozo, si no se establece:

  # Python 3.6, line 378 in `multiprocessing.pool.py` if chunksize is None: chunksize, extra = divmod(len(iterable), len(self._pool) * 4) if extra: chunksize += 1 if len(iterable) == 0: chunksize = 0