La mayoría de las funciones de Numpy permitirán el subprocesamiento múltiple de forma predeterminada.
por ejemplo, trabajo en una estación de trabajo Intel CPU de 8 núcleos, si ejecuto un script
import numpy as np
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
Linux top
mostrará un uso de CPU del 800% durante la ejecución, lo
que significa que numpy detecta automáticamente que mi estación de trabajo tiene 8 núcleos y np.sqrt
usa automáticamente los 8 núcleos para acelerar el cálculo.
Sin embargo, encontré un error extraño. Si ejecuto un script
import numpy as np
import pandas as pd
df=pd.DataFrame(np.random.random((10,10)))
df+df
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
¡El uso de la CPU es 100%! Esto significa que si además de dos pandas DataFrame antes de ejecutar cualquier función numpy, la función de subprocesamiento automático de numpy desaparece sin previo aviso. Esto no es absolutamente razonable, ¿por qué el cálculo del marco de datos de Pandas afectaría la configuración de subprocesos Numpy? ¿Es un error? ¿Cómo solucionar esto?
PD:
Excavo más con la perf
herramienta Linux .
ejecutando el primer script muestra
Mientras se ejecuta el segundo script muestra
Por lo tanto libmkl_vml_avx2.so
, ambas secuencias de comandos implican , mientras que la primera secuencia de comandos implica adicional libiomp5.so
que parece estar relacionada con openMP.
Y dado que vml significa intel vector math library, de acuerdo con vml doc, supongo que al menos las funciones a continuación son multiproceso automáticamente
import numpy as np import pandas as pd import os os.environ["MKL_NUM_THREADS"] = '4' print(os.environ["MKL_NUM_THREADS"]) df=pd.DataFrame(np.random.random((10,10))) df+df print(os.environ["MKL_NUM_THREADS"]) a = np.random.random((20000000, 3)) b = np.random.random((3, 30)) for _ in range(10): c = np.dot(a, b)
Respuestas:
Pandas utiliza
numexpr
debajo del capó para calcular algunas operaciones ynumexpr
establece el número máximo de subprocesos para vml en 1, cuando se importa :y los pandas lo importan cuando
df+df
se evalúa en expressions.py :Sin embargo, la distribución Anaconda también utiliza VML-funcionalidad para funciones tales como
sqrt
,sin
,cos
y así sucesivamente - y una veznumexpr
establecido el número máximo de VML-hilos para 1, los numpy-funciones ya no uso paralelización.El problema se puede ver fácilmente en gdb (usando su script lento):
es decir, podemos ver,
numexpr
establece el número de subprocesos en 1. Que luego se usa cuando se llama a la función vml-sqrt:Entonces, podemos ver que Numpy utiliza la implementación de vml,
vdSqrt
que utilizamkl_vml_serv_threader_d_1i_1o
para decidir si el cálculo debe hacerse en paralelo y si se ve el número de subprocesos:el registro
%rax
tiene el número máximo de hilos y es 1.Ahora podemos usar
numexpr
para aumentar el número de hilos vml , es decir:¡Ahora se utilizan múltiples núcleos!
fuente
numexpr
detrás de escena.Mirando numpy, parece que, bajo el capó, ha tenido problemas de activación / desactivación con subprocesos múltiples, y dependiendo de la versión que esté utilizando, puede esperar ver bloqueos cuando sube ne.set_vml_num_threads () ..
http://numpy-discussion.10968.n7.nabble.com/ANN-NumExpr-2-7-0-Release-td47414.html
Necesito entender cómo esto está pegado al intérprete de Python, dado su ejemplo de código donde parece que de alguna manera parece permitir que varias llamadas aparentemente sincrónicas / ordenadas a np.sqrt () procedan en paralelo. Supongo que si el intérprete de Python siempre está devolviendo una referencia a un objeto cuando aparece la pila, y en su ejemplo, solo presenta esas referencias y no las asigna o manipula de ninguna manera, estaría bien. Pero si las iteraciones de bucle posteriores dependen de las anteriores, entonces parece menos claro cómo se podrían paralelizar de forma segura. Podría decirse que la falla silenciosa / los resultados incorrectos son peores que los accidentes.
fuente
Creo que su premisa inicial puede ser incorrecta:
Usted dijo: Lo que significa que numpy detecta automáticamente que mi estación de trabajo tiene 8 núcleos, y np.sqrt usa automáticamente los 8 núcleos para acelerar el cálculo.
Una sola función np.sqrt () no puede adivinar cómo se invocará o devolverá antes de que se haya completado parcialmente. Hay mecanismos de paralelismo en python, pero ninguno es automático.
Ahora, dicho esto, el intérprete de Python puede optimizar el bucle for para el paralelismo, que puede ser lo que está viendo, pero sospecho que si observa el tiempo del reloj de pared para que este bucle se ejecute, no habrá diferente independientemente de si está (aparentemente) usando 8 núcleos o 1 núcleo.
ACTUALIZACIÓN: Después de leer un poco más de los comentarios, parece que el comportamiento multinúcleo que está viendo está relacionado con la distribución anaconda del intérprete de Python. Le eché un vistazo, pero no pude encontrar ningún código fuente, pero parece que la licencia de Python permite a las entidades (como anaconda.com) compilar y distribuir derivados del intérprete sin requerir que se publiquen sus cambios.
Supongo que puede comunicarse con la gente de la anaconda: el comportamiento que está viendo será difícil de entender sin saber qué / si algo han cambiado en el intérprete.
También verifique rápidamente el tiempo del reloj de pared con / sin la optimización para ver si realmente es 8 veces más rápido; incluso si realmente tiene los 8 núcleos funcionando en lugar de 1, sería bueno saber si los resultados son realmente 8x más rápido o si hay bloqueos de giro en uso que todavía se están serializando en un único mutex.
fuente