Me gustaría escribir un programa que haga un uso extensivo de las funcionalidades de álgebra lineal BLAS y LAPACK. Dado que el rendimiento es un problema, hice una evaluación comparativa y me gustaría saber si el enfoque que tomé es legítimo.
Tengo, por así decirlo, tres concursantes y quiero probar su desempeño con una simple multiplicación matriz-matriz. Los concursantes son:
- Numpy, haciendo uso únicamente de la funcionalidad de
dot
. - Python, llamando a las funcionalidades BLAS a través de un objeto compartido.
- C ++, llamando a las funcionalidades BLAS a través de un objeto compartido.
Guión
Implementé una multiplicación matriz-matriz para diferentes dimensiones i
. i
va de 5 a 500 con un incremento de 5 y las matrices m1
y m2
se configuran así:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Numpy
El código utilizado se ve así:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, llamando a BLAS a través de un objeto compartido
Con la función
_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):
no_trans = c_char("n")
n = c_int(i)
one = c_float(1.0)
zero = c_float(0.0)
_blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref(n), byref(n),
byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n),
m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero),
r.ctypes.data_as(ctypes.c_void_p), byref(n))
el código de prueba se ve así:
r = numpy.zeros((i,i), numpy.float32)
tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul")
rBlas.append((i, tBlas.repeat(20, 1)))
3. c ++, llamando a BLAS a través de un objeto compartido
Ahora el código c ++ naturalmente es un poco más largo, así que reduzco la información al mínimo.
Cargo la función con
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Mido el tiempo gettimeofday
así:
gettimeofday(&start, NULL);
f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim);
gettimeofday(&end, NULL);
dTimes[j] = CalcTime(start, end);
donde j
hay un bucle que se ejecuta 20 veces. Calculo el tiempo transcurrido con
double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}
Resultados
El resultado se muestra en la siguiente gráfica:
Preguntas
- ¿Crees que mi enfoque es justo o hay algunos gastos generales innecesarios que puedo evitar?
- ¿Esperaría que el resultado mostrara una discrepancia tan grande entre el enfoque de c ++ y python? Ambos utilizan objetos compartidos para sus cálculos.
- Como prefiero usar Python para mi programa, ¿qué puedo hacer para aumentar el rendimiento al llamar a las rutinas BLAS o LAPACK?
Descargar
El benchmark completo se puede descargar aquí . (JF Sebastian hizo posible ese enlace ^^)
r
matriz es injusta. Estoy resolviendo el "problema" ahora mismo y publico los nuevos resultados.np.ascontiguousarray()
(considere el orden C vs. Fortran). 2. asegúrese de quenp.dot()
utiliza el mismolibblas.so
.m1
ym2
tienen elascontiguousarray
indicador comoTrue
. Y numpy usa el mismo objeto compartido que C lo hace. En cuanto al orden de la matriz: actualmente no estoy interesado en el resultado del cálculo, por lo tanto, el orden es irrelevante.Respuestas:
He ejecutado su punto de referencia . No hay diferencia entre C ++ y numpy en mi máquina:
Parece justo porque no hay diferencia en los resultados.
No.
Asegúrese de que numpy utilice una versión optimizada de las bibliotecas BLAS / LAPACK en su sistema.
fuente
ACTUALIZACIÓN (30.07.2014):
Vuelvo a ejecutar el punto de referencia en nuestro nuevo HPC. Tanto el hardware como la pila de software cambiaron de la configuración en la respuesta original.
Puse los resultados en una hoja de cálculo de Google (contiene también los resultados de la respuesta original).
Hardware
Nuestro HPC tiene dos nodos diferentes, uno con CPU Intel Sandy Bridge y otro con las CPU Ivy Bridge más nuevas:
Sandy (MKL, OpenBLAS, ATLAS):
Ivy (MKL, OpenBLAS, ATLAS):
Software
La pila de software es para ambos nodos, sam. En lugar de GotoBLAS2 , se utiliza OpenBLAS y también hay un ATLAS BLAS de subprocesos múltiples que se establece en 8 subprocesos (codificado).
Benchmark de producto escalar
El código de referencia es el mismo que se muestra a continuación. Sin embargo, para las nuevas máquinas también ejecuté el punto de referencia para los tamaños de matriz 5000 y 8000 .
La siguiente tabla incluye los resultados de referencia de la respuesta original (renombrado: MKL -> Nehalem MKL, Netlib Blas -> Nehalem Netlib BLAS, etc.)
Rendimiento de un solo hilo:
Rendimiento de subprocesos múltiples (8 subprocesos):
Hilos frente al tamaño de la matriz (Ivy Bridge MKL) :
Suite Benchmark
Rendimiento de un solo hilo:
Rendimiento de subprocesos múltiples (8 subprocesos):
Conclusión
Los nuevos resultados de referencia son similares a los de la respuesta original. OpenBLAS y MKL funcionan al mismo nivel, con la excepción de la prueba Eigenvalue . Los autovalor realiza la prueba solamente razonablemente bien en OpenBLAS en el modo de un solo subproceso . En el modo de subprocesos múltiples, el rendimiento es peor.
El "gráfico de tamaño de matriz frente a subprocesos" también muestra que aunque MKL y OpenBLAS generalmente escalan bien con el número de núcleos / subprocesos, depende del tamaño de la matriz. Para matrices pequeñas, agregar más núcleos no mejorará mucho el rendimiento.
También hay un aumento de rendimiento de aproximadamente un 30% desde Sandy Bridge hasta Ivy Bridge, lo que podría deberse a una mayor frecuencia de reloj (+ 0,8 Ghz) y / o una mejor arquitectura.
Respuesta original (04.10.2011):
Hace algún tiempo tuve que optimizar algunos cálculos / algoritmos de álgebra lineal que se escribieron en Python usando numpy y BLAS, así que comparé / probé diferentes configuraciones de numpy / BLAS.
Específicamente probé:
Ejecuté dos puntos de referencia diferentes:
Aquí están mis resultados:
Máquinas
Linux (MKL, ATLAS, No-MKL, GotoBlas2):
Mac Book Pro (Accelerate Framework):
Servidor Mac (Accelerate Framework):
Punto de referencia de producto escalar
Codigo :
Resultados :
Suite Benchmark
Código :
para obtener información adicional sobre la suite de referencia, consulte aquí .
Resultados :
Instalación
La instalación de MKL incluyó la instalación completa de Intel Compiler Suite, que es bastante sencilla. Sin embargo, debido a algunos errores / problemas, configurar y compilar numpy con soporte MKL fue un poco complicado.
GotoBlas2 es un pequeño paquete que se puede compilar fácilmente como una biblioteca compartida. Sin embargo, debido a un error , debe volver a crear la biblioteca compartida después de construirla para usarla con numpy.
Además de esta construcción, para múltiples plataformas de destino no funcionó por alguna razón. Así que tuve que crear un archivo .so para cada plataforma para la que quiero tener un archivo libgoto2.so optimizado .
Si instala numpy desde el repositorio de Ubuntu, se instalará y configurará automáticamente numpy para usar ATLAS . La instalación de ATLAS desde la fuente puede llevar algún tiempo y requiere algunos pasos adicionales (fortran, etc.).
Si instala numpy en una máquina Mac OS X con puertos Fink o Mac , configurará numpy para usar ATLAS o Accelerate Framework de Apple . Puede verificar ejecutando ldd en el archivo numpy.core._dotblas o llamando a numpy.show_config () .
Conclusiones
MKL funciona mejor seguido de cerca por GotoBlas2 .
En la prueba de valor propio , GotoBlas2 se comporta sorprendentemente peor de lo esperado. No estoy seguro de por qué es así.
Accelerate Framework de Apple funciona realmente bien, especialmente en el modo de un solo subproceso (en comparación con las otras implementaciones de BLAS).
Tanto GotoBlas2 como MKL escalan muy bien con el número de subprocesos. Entonces, si tiene que lidiar con matrices grandes, ejecutarlo en varios subprocesos será de gran ayuda.
En cualquier caso, no use la implementación predeterminada de netlib blas porque es demasiado lenta para cualquier trabajo computacional serio.
En nuestro clúster también instalé ACML de AMD y el rendimiento fue similar al de MKL y GotoBlas2 . No tengo números difíciles.
Personalmente, recomendaría usar GotoBlas2 porque es más fácil de instalar y es gratis.
Si desea codificar en C ++ / C, también consulte Eigen3, que se supone que supera a MKL / GotoBlas2 en algunos casos y también es bastante fácil de usar.
fuente
Aquí hay otro punto de referencia (en Linux, simplemente escriba
make
): http://dl.dropbox.com/u/5453551/blas_call_benchmark.ziphttp://dl.dropbox.com/u/5453551/blas_call_benchmark.png
No veo esencialmente ninguna diferencia entre los diferentes métodos para matrices grandes, entre Numpy, Ctypes y Fortran. (Fortran en lugar de C ++ --- y si esto importa, su punto de referencia probablemente esté roto).
Su¿Quizás su punto de referencia también tiene otros errores, por ejemplo, comparar entre diferentes bibliotecas BLAS o diferentes configuraciones BLAS como el número de subprocesos, o entre el tiempo real y el tiempo de la CPU?CalcTime
función en C ++ parece tener un error de signo.... + ((double)start.tv_usec))
debería ser en su lugar... - ((double)start.tv_usec))
.EDITAR : no se pudieron contar las llaves en la
CalcTime
función, está bien.Como pauta: si realiza una evaluación comparativa, publique siempre todo el código en algún lugar. Comentar los puntos de referencia, especialmente cuando sorprende, sin tener el código completo, no suele ser productivo.
Para saber contra qué BLAS Numpy está vinculado, haga lo siguiente:
ACTUALIZACIÓN : si no puede importar numpy.core._dotblas, su Numpy está usando su copia de respaldo interna de BLAS, que es más lenta y no está diseñada para usarse en computación de rendimiento. La respuesta de @Woltan a continuación indica que esta es la explicación de la diferencia que ve en Numpy vs.Ctypes + BLAS.
Para solucionar la situación, necesita ATLAS o MKL --- consulte estas instrucciones: http://scipy.org/Installing_SciPy/Linux La mayoría de las distribuciones de Linux se envían con ATLAS, por lo que la mejor opción es instalar su
libatlas-dev
paquete (el nombre puede variar) .fuente
import numpy.core._dotblas
. Cual podría ser el problema aquí? Intentaré limpiar mi punto de referencia y escribir un archivo MAKE para que otros lo prueben.otool -L
lugar deldd
en LinuxDado el rigor que ha demostrado con su análisis, me sorprenden los resultados hasta ahora. Pongo esto como una 'respuesta' pero solo porque es demasiado largo para un comentario y brinda una posibilidad (aunque espero que lo hayas considerado).
Hubiera pensado que el enfoque numpy / python no agregaría mucha sobrecarga para una matriz de complejidad razonable, ya que a medida que aumenta la complejidad, la proporción en la que participa Python debería ser pequeña. Estoy más interesado en los resultados en el lado derecho del gráfico, pero una discrepancia de órdenes de magnitud mostrada allí sería inquietante.
Me pregunto si está utilizando los mejores algoritmos que numpy puede aprovechar. De la guía de compilación para linux:
"Construir FFTW (3.1.2): Versiones SciPy> = 0.7 y Numpy> = 1.2: Debido a problemas de licencia, configuración y mantenimiento, se eliminó el soporte para FFTW en las versiones de SciPy> = 0.7 y NumPy> = 1.2. En su lugar, ahora se usa una versión incorporada de fftpack. Hay un par de formas de aprovechar la velocidad de FFTW si es necesario para su análisis. Cambie a una versión Numpy / Scipy que incluya soporte. Instale o cree su propio contenedor de FFTW. Consulte http: //developer.berlios.de/projects/pyfftw/ como un ejemplo no respaldado ".
¿Compilaste numpy con mkl? ( http://software.intel.com/en-us/articles/intel-mkl/ ). Si está ejecutando en linux, las instrucciones para compilar numpy con mkl están aquí: http://www.scipy.org/Installing_SciPy/Linux#head-7ce43956a69ec51c6f2cedd894a4715d5bfff974 (a pesar de la URL). La parte clave es:
Si está en Windows, puede obtener un binario compilado con mkl, (y también obtener pyfftw y muchos otros algoritmos relacionados) en: http://www.lfd.uci.edu/~gohlke/pythonlibs/ , con un deuda de gratitud con Christoph Gohlke en el Laboratorio de Dinámica de Fluorescencia, UC Irvine.
Advertencia, en cualquier caso, hay muchos problemas de licencia y demás que debe tener en cuenta, pero la página de inteligencia los explica. Nuevamente, imagino que ha considerado esto, pero si cumple con los requisitos de licencia (que en Linux es muy fácil de hacer), esto aceleraría mucho la parte numpy en relación con el uso de una compilación automática simple, sin siquiera FFTW. Me interesará seguir este hilo y ver qué piensan los demás. Independientemente, excelente rigor y excelente pregunta. Gracias por publicarlo.
fuente