Importación de scipy rompe el soporte de multiprocesamiento en Python

Me estoy topando con un problema extraño que no puedo explicar. ¡Espero que alguien pueda ayudar por favor!

Estoy ejecutando Python 2.7.3 y Scipy v0.14.0 y estoy intentando implementar algunos algoritmos multiprocesador muy simples para acelerar mi código mediante el multiprocessing módulo. He logrado hacer funcionar un ejemplo básico:

 import multiprocessing import numpy as np import time # import scipy.special def compute_something(t): a = 0. for i in range(100000): a = np.sqrt(t) return a if __name__ == '__main__': pool_size = multiprocessing.cpu_count() print "Pool size:", pool_size pool = multiprocessing.Pool(processes=pool_size) inputs = range(10) tic = time.time() builtin_outputs = map(compute_something, inputs) print 'Built-in:', time.time() - tic tic = time.time() pool_outputs = pool.map(compute_something, inputs) print 'Pool :', time.time() - tic 

Esto funciona bien, volviendo

 Pool size: 8 Built-in: 1.56904006004 Pool : 0.447728157043 

Pero si descomento la línea de import scipy.special , obtengo:

 Pool size: 8 Built-in: 1.58968091011 Pool : 1.59387993813 

y puedo ver que solo un núcleo está haciendo el trabajo en mi sistema. De hecho, importar cualquier módulo del paquete scipy parece tener este efecto (he intentado varios).

¿Algunas ideas? Nunca antes había visto un caso como este, donde una importación aparentemente inocua puede tener un efecto tan extraño e inesperado.

¡Gracias!

Actualizar (1)

Mover la línea de importación de scipy a la función compute_something mejora parcialmente el problema:

 Pool size: 8 Built-in: 1.66807389259 Pool : 0.596321105957 

Actualizar (2)

Gracias a @larsmans por probar en un sistema diferente. El problema no fue confirmado usando Scipy v.0.12.0. Mover esta consulta a la lista de correo scipy y publicará cualquier respuesta.

Después de investigar mucho y publicar un problema en el sitio de Scipy GitHub, encontré una solución.

Antes de comenzar, esto se documenta muy bien aquí , solo daré una descripción general.

Este problema no está relacionado con la versión de Scipy o Numpy que estaba usando. Se origina en las bibliotecas BLAS del sistema que Numpy y Scipy utilizan para varias rutinas de álgebra lineal. Puede saber a qué bibliotecas está vinculada Numpy ejecutando

python -c 'import numpy; numpy.show_config()'

Si está utilizando OpenBLAS en Linux, puede encontrar que la afinidad de la CPU se establece en 1, lo que significa que una vez que estos algoritmos se importan en Python (a través de Numpy / Scipy), puede acceder a la mayoría de un núcleo de la CPU. Para probar esto, dentro de un terminal de Python ejecutado

 import os os.system('taskset -p %s' %os.getpid()) 

Si la afinidad de la CPU se devuelve como f , de ff , puede acceder a múltiples núcleos. En mi caso, comenzaría así, pero al importar numpy o scipy.any_module, cambiaría a 1 , de ahí mi problema.

He encontrado dos soluciones:

Cambiar la afinidad de la CPU

Puede configurar manualmente la afinidad de la CPU del proceso maestro en la parte superior de la función principal para que el código tenga este aspecto:

 import multiprocessing import numpy as np import math import time import os def compute_something(t): a = 0. for i in range(10000000): a = math.sqrt(t) return a if __name__ == '__main__': pool_size = multiprocessing.cpu_count() os.system('taskset -cp 0-%d %s' % (pool_size, os.getpid())) print "Pool size:", pool_size pool = multiprocessing.Pool(processes=pool_size) inputs = range(10) tic = time.time() builtin_outputs = map(compute_something, inputs) print 'Built-in:', time.time() - tic tic = time.time() pool_outputs = pool.map(compute_something, inputs) print 'Pool :', time.time() - tic 

Tenga en cuenta que la selección de un valor superior al número de núcleos para el taskset de taskset no parece importar, solo utiliza el número máximo posible.

Cambiar bibliotecas BLAS

Solución documentada en el sitio vinculado anteriormente. Básicamente: instale libatlas y ejecute update-alternatives para señalar numpy a ATLAS en lugar de OpenBLAS.