¿Por qué el multiprocesamiento usa solo un núcleo después de importar numpy?

127

No estoy seguro de si esto cuenta más como un problema del sistema operativo, pero pensé en preguntar aquí en caso de que alguien tenga alguna idea del final de Python.

He estado tratando de paralelizar un forciclo de CPU pesado usando joblib, pero encuentro que en lugar de que cada proceso de trabajo se asigne a un núcleo diferente, termino con todos ellos asignados al mismo núcleo y sin ganancia de rendimiento.

Aquí hay un ejemplo muy trivial ...

from joblib import Parallel,delayed
import numpy as np

def testfunc(data):
    # some very boneheaded CPU work
    for nn in xrange(1000):
        for ii in data[0,:]:
            for jj in data[1,:]:
                ii*jj

def run(niter=10):
    data = (np.random.randn(2,100) for ii in xrange(niter))
    pool = Parallel(n_jobs=-1,verbose=1,pre_dispatch='all')
    results = pool(delayed(testfunc)(dd) for dd in data)

if __name__ == '__main__':
    run()

... y esto es lo que veo htopmientras se ejecuta este script:

htop

Estoy ejecutando Ubuntu 12.10 (3.5.0-26) en una computadora portátil con 4 núcleos. Claramente joblib.Parallelestá generando procesos separados para los diferentes trabajadores, pero ¿hay alguna forma de que pueda ejecutar estos procesos en diferentes núcleos?

ali_m
fuente
stackoverflow.com/questions/15168014/… - no hay respuestas allí, me temo, pero parece el mismo problema.
NPE
¿Sigue siendo un problema? Estoy intentando recrear esto con Python 3.7 e importar numpy con multiprocesamiento.Pool (), y está usando todos los hilos (como debería). Solo quiero asegurarme de que esto se haya solucionado.
Jared Nielsen

Respuestas:

148

Después de buscar más en Google, encontré la respuesta aquí .

Resulta que ciertos módulos de Python ( numpy, scipy, tables, pandas, skimagedesorden ...) con afinidad central en la importación. Por lo que puedo decir, este problema parece ser causado específicamente por su vinculación con bibliotecas OpenBLAS multiproceso.

Una solución alternativa es restablecer la afinidad de la tarea utilizando

os.system("taskset -p 0xff %d" % os.getpid())

Con esta línea pegada después de las importaciones del módulo, mi ejemplo ahora se ejecuta en todos los núcleos:

htop_workaround

Mi experiencia hasta ahora ha sido que esto no parece tener ningún efecto negativo en numpyel rendimiento, aunque esto probablemente sea específico de la máquina y la tarea.

Actualizar:

También hay dos formas de deshabilitar el comportamiento de restablecimiento de afinidad de CPU de OpenBLAS. En tiempo de ejecución puede usar la variable de entorno OPENBLAS_MAIN_FREE(o GOTOBLAS_MAIN_FREE), por ejemplo

OPENBLAS_MAIN_FREE=1 python myscript.py

O, alternativamente, si está compilando OpenBLAS desde la fuente, puede deshabilitarlo permanentemente en el momento de la compilación editando el Makefile.rulepara contener la línea

NO_AFFINITY=1
ali_m
fuente
Gracias, su solución resolvió el problema. Una pregunta, tengo el mismo código pero se ejecuta de manera diferente en una máquina diferente. Ambas máquinas son Ubuntu 12.04 LTS, python 2.7, pero solo una tiene este problema. ¿Tienes alguna idea de por qué?
iampat
Ambas máquinas tienen OpenBLAS (compilado con OpenMPI).
iampat
2
Hilo antiguo, pero en caso de que alguien más encuentre este problema, tuve el problema exacto y de hecho estaba relacionado con las bibliotecas OpenBLAS. Vea aquí dos posibles soluciones alternativas y algunas discusiones relacionadas.
Gabriel
2
Otra forma de establecer la afinidad de la CPU es usarpsutil .
Ioannis Filippidis
2
@JHG Es un problema con OpenBLAS en lugar de Python, así que no puedo ver ninguna razón por la versión de Python haría una diferencia
ali_m
27

Python 3 ahora expone los métodos para establecer directamente la afinidad

>>> import os
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}
>>> os.sched_setaffinity(0, {1, 3})
>>> os.sched_getaffinity(0)
{1, 3}
>>> x = {i for i in range(10)}
>>> x
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> os.sched_setaffinity(0, x)
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}
WoJ
fuente
1
Error> AttributeError: el módulo 'os' no tiene el atributo 'sched_getaffinity', Python 3.6
Paddy el
44
@ Paddy De la documentación vinculada: solo están disponibles en algunas plataformas Unix.
BlackJack
2
Tengo el mismo problema, pero he integrado esta misma línea en el sistema os.stop superior ("taskset -p 0xff% d"% os.getpid ()) pero no usa todas las CPU
rajeshcis
12

Esto parece ser un problema común con Python en Ubuntu, y no es específico para joblib:

Sugeriría experimentar con afinidad de CPU ( taskset).

NPE
fuente
Python on UbuntuEsto implica que funciona sin problemas en Windows y otros sistemas operativos. ¿Lo es?
Mástil