c++ - velocidad - python vs c# español
Benchmarking(python vs. c++ usando BLAS) y(numpy) (4)
Me gustaría escribir un programa que hace un uso extensivo de las funcionalidades de álgebra lineal BLAS y LAPACK. Como el rendimiento es un problema, realicé algunas evaluaciones comparativas y me gustaría saber si el enfoque que tomé es legítimo.
Tengo, por así decirlo, tres concursantes y quiero probar su rendimiento con una simple multiplicación matriz-matriz. Los concursantes son:
- Numpy, haciendo uso solo de la funcionalidad del
dot
. - Python, llamando a las funcionalidades de BLAS a través de un objeto compartido.
- C ++, llamando a las funcionalidades de BLAS a través de un objeto compartido.
Guión
Implementé una multiplicación matriz-matriz para diferentes dimensiones i
. funciono 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 usado 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, naturalmente, el código de c ++ es un poco más largo, así que reduzco la información a un mínimo.
Cargo la función con
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Mido el tiempo con 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
es un bucle que se ejecuta 20 veces. Calculo el tiempo pasado 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 el siguiente diagrama:
Preguntas
- ¿Crees que mi enfoque es justo o hay algunos gastos indirectos innecesarios que puedo evitar?
- ¿Esperaría que el resultado muestre una discrepancia tan grande entre el enfoque de c ++ y python? Ambos están utilizando objetos compartidos para sus cálculos.
- Como prefiero usar Python para mi programa, ¿qué podría hacer para aumentar el rendimiento cuando llamo a BLAS o LAPACK?
Descargar
El punto de referencia completo se puede descargar here . (JF Sebastian hizo posible ese enlace ^^)
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 (también contiene los resultados de la respuesta original).
Hardware
Nuestro HPC tiene dos nodos diferentes, uno con CPU Intel Sandy Bridge y el otro con las nuevas CPU Ivy Bridge:
Sandy (MKL, OpenBLAS, ATLAS):
- CPU : 2 x 16 Intel (R) Xeon (R) E2560 Sandy Bridge a 2.00 GHz (16 núcleos)
- RAM : 64 GB
Ivy (MKL, OpenBLAS, ATLAS):
- CPU : 2 x 20 Intel (R) Xeon (R) E2680 V2 Ivy Bridge a 2.80 GHz (20 núcleos, con HT = 40 núcleos)
- RAM : 256 GB
Software
La pila de software es para ambos nodos, el sam. En lugar de GotoBLAS2 , se usa OpenBLAS y también hay un ATLAS BLAS de subprocesos múltiples configurado en 8 subprocesos (codificado).
- OS : Suse
- Compilador de Intel : ictce-5.3.0
- Numpy: 1.8.0
- OpenBLAS: 0.2.6
- ATLAS :: 3.8.4
Punto de referencia del producto
Benchmark-code es el mismo que a continuación. Sin embargo, para las nuevas máquinas también ejecuté el punto de referencia para tamaños de matriz 5000 y 8000 .
La siguiente tabla incluye los resultados del benchmark de la respuesta original (renombrado: MKL -> Nehalem MKL, Netlib Blas -> Nehalem Netlib BLAS, etc.)
Rendimiento de rosca simple:
Rendimiento multi-hilo (8 hilos):
Hilos frente a tamaño de matriz (Ivy Bridge MKL) :
Suite Benchmark
Rendimiento de rosca simple:
Rendimiento multi hilo (8 hilos):
Conclusión
Los nuevos resultados de referencia son similares a los de la respuesta original. OpenBLAS y MKL funcionan en el mismo nivel, con la excepción de la prueba de valor propio. La prueba Eigenvalue funciona solo razonablemente bien en OpenBLAS en modo de un solo hilo . 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 se adaptan bien al 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 aproximadamente un 30% de aumento en el rendimiento de Sandy Bridge a Ivy Bridge, que podría deberse a una mayor frecuencia de reloj (+ 0,8 Ghz) y / o una mejor arquitectura.
Respuesta original (10.04.2011):
Hace algún tiempo tuve que optimizar algunos cálculos / algoritmos de álgebra lineal que se escribieron en python usando numpy y BLAS, por lo que evalué / probé diferentes configuraciones de numpy / BLAS.
Específicamente probé:
- Numpy con ATLAS
- Numpy con GotoBlas2 (1.13)
- Numpy con MKL (11.1 / 073)
- Numpy con Accelerate Framework (Mac OS X)
Ejecuté dos puntos de referencia diferentes:
- producto punto simple de matrices con diferentes tamaños
- Suite de referencia que se puede encontrar here .
Aquí están mis resultados:
Máquinas
Linux (MKL, ATLAS, No-MKL, GotoBlas2):
- Sistema operativo : Ubuntu Lucid 10.4 64 Bit.
- CPU : 2 x 4 Intel (R) Xeon (R) E5504 a 2.00 GHz (8 núcleos)
- RAM : 24 GB
- Compilador Intel : 11.1 / 073
- Scipy : 0.8
- Numpy : 1.5
Mac Book Pro (Accelerate Framework):
- Sistema operativo : Mac OS X Snow Leopard (10.6)
- CPU : 1 Intel Core 2 Duo 2,93 Ghz (2 núcleos)
- RAM : 4 GB
- Scipy : 0.7
- Numpy : 1.3
Servidor Mac (Accelerate Framework):
- Sistema operativo : Mac OS X Snow Leopard Server (10.6)
- CPU : 4 X Intel (R) Xeon (R) E5520 a 2.26 Ghz (8 núcleos)
- RAM : 4 GB
- Scipy : 0.8
- Numpy : 1.5.1
Punto de referencia del producto
Código :
import numpy as np
a = np.random.random_sample((size,size))
b = np.random.random_sample((size,size))
%timeit np.dot(a,b)
Resultados :
System | size = 1000 | size = 2000 | size = 3000 | netlib BLAS | 1350 ms | 10900 ms | 39200 ms | ATLAS (1 CPU) | 314 ms | 2560 ms | 8700 ms | MKL (1 CPUs) | 268 ms | 2110 ms | 7120 ms | MKL (2 CPUs) | - | - | 3660 ms | MKL (8 CPUs) | 39 ms | 319 ms | 1000 ms | GotoBlas2 (1 CPU) | 266 ms | 2100 ms | 7280 ms | GotoBlas2 (2 CPUs)| 139 ms | 1009 ms | 3690 ms | GotoBlas2 (8 CPUs)| 54 ms | 389 ms | 1250 ms | Mac OS X (1 CPU) | 143 ms | 1060 ms | 3605 ms | Mac Server (1 CPU)| 92 ms | 714 ms | 2130 ms |
Suite Benchmark
Código :
Para obtener información adicional sobre el conjunto de referencia, consulte here .
Resultados :
System | eigenvalues | svd | det | inv | dot | netlib BLAS | 1688 ms | 13102 ms | 438 ms | 2155 ms | 3522 ms | ATLAS (1 CPU) | 1210 ms | 5897 ms | 170 ms | 560 ms | 893 ms | MKL (1 CPUs) | 691 ms | 4475 ms | 141 ms | 450 ms | 736 ms | MKL (2 CPUs) | 552 ms | 2718 ms | 96 ms | 267 ms | 423 ms | MKL (8 CPUs) | 525 ms | 1679 ms | 60 ms | 137 ms | 197 ms | GotoBlas2 (1 CPU) | 2124 ms | 4636 ms | 147 ms | 456 ms | 743 ms | GotoBlas2 (2 CPUs)| 1560 ms | 3278 ms | 116 ms | 295 ms | 460 ms | GotoBlas2 (8 CPUs)| 741 ms | 2914 ms | 82 ms | 262 ms | 192 ms | Mac OS X (1 CPU) | 948 ms | 4339 ms | 151 ms | 318 ms | 566 ms | Mac Server (1 CPU)| 1033 ms | 3645 ms | 99 ms | 232 ms | 342 ms |
Instalación
La instalación de MKL incluyó la instalación de la completa Intel Compiler Suite, que es bastante sencilla. Sin embargo, debido a algunos errores / problemas de configuración y compilación de numpy con soporte MKL fue un poco molesto.
GotoBlas2 es un pequeño paquete que se puede compilar fácilmente como una biblioteca compartida. Sin embargo, debido a un bug , debe volver a crear la biblioteca compartida después de compilarla para poder usarla con numpy.
Además de este edificio, por plataforma de objetivo múltiple no funcionó por alguna razón. Así que tuve que crear un archivo .so para cada plataforma para la que quiero tener un archivo optimizado libgoto2.so .
Si instala numpy desde el repositorio de Ubuntu, automáticamente instalará y configurará numpy para usar ATLAS . Instalar ATLAS desde la fuente puede llevar algo de tiempo y requiere algunos pasos adicionales (fortran, etc.).
Si instala Numpy en una máquina Mac OS X con Fink o Mac Ports , configurará numpy para usar ATLAS o Apple''s Accelerate Framework . 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 autovalores , GotoBlas2 tiene un rendimiento sorprendentemente peor de lo esperado. No estoy seguro de por qué este es el caso.
Apple''s Accelerate Framework funciona muy bien especialmente en modo de un solo hilo (en comparación con las otras implementaciones de BLAS).
Tanto GotoBlas2 como MKL se adaptan muy bien con el número de subprocesos. Entonces, si tiene que lidiar con matrices grandes, ejecutarlo en múltiples hilos ayudará mucho.
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é el ACML de AMD y el rendimiento fue similar al de MKL y GotoBlas2 . No tengo ningún número difícil.
Yo 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 en algunos cases se supone que superará a MKL / GotoBlas2 y también es bastante fácil de usar.
Aquí hay otro punto de referencia (en Linux, simplemente escriba make
): http://dl.dropbox.com/u/5453551/blas_call_benchmark.zip
http://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 función CalcTime
en C ++ parece tener un error de signo. Tal vez su punto de referencia también tiene otros errores, por ejemplo, la comparación entre diferentes bibliotecas BLAS, o diferentes configuraciones BLAS como el número de subprocesos, o entre el tiempo real y el tiempo de CPU? ... + ((double)start.tv_usec))
debería ser en su lugar ... - ((double)start.tv_usec))
.
EDITAR : no se pudieron contar las llaves en la función CalcTime
, está bien.
Como guía: si hace un punto de referencia, por favor siempre publique todo el código en alguna parte. Comentar sobre los puntos de referencia, especialmente cuando es sorprendente, sin tener el código completo generalmente no es productivo.
Para saber con qué BLAS Numpy está vinculado, hazlo:
$ python Python 2.7.2+ (default, Aug 16 2011, 07:24:41) [GCC 4.6.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import numpy.core._dotblas >>> numpy.core._dotblas.__file__ ''/usr/lib/pymodules/python2.7/numpy/core/_dotblas.so'' >>> $ ldd /usr/lib/pymodules/python2.7/numpy/core/_dotblas.so linux-vdso.so.1 => (0x00007fff5ebff000) libblas.so.3gf => /usr/lib/libblas.so.3gf (0x00007fbe618b3000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbe61514000)
ACTUALIZACIÓN : si no puede importar numpy.core._dotblas, su Numpy está utilizando su copia de seguridad interna de BLAS, que es más lenta y no está destinada a ser utilizada en la informática 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 paquete libatlas-dev
(nombre puede variar).
Dado el rigor que ha demostrado con su análisis, estoy sorprendido por los resultados hasta el momento. Puse esto como una ''respuesta'', pero solo porque es demasiado largo para un comentario y ofrece una posibilidad (aunque espero que lo hayas considerado).
Hubiera pensado que el enfoque numpy / python no agregaría demasiada 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 la discrepancia de órdenes de magnitud que se muestra allí sería inquietante.
Me pregunto si estás usando los mejores algoritmos que Numpy puede aprovechar. De la guía de compilación para Linux:
"Build FFTW (3.1.2): versiones de SciPy> = 0.7 y Numpy> = 1.2: Debido a problemas de licencia, configuración y mantenimiento, se eliminó el soporte para FFTW en 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 maneras de aprovechar la velocidad de FFTW si es necesario para su análisis. Baje a una versión de 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. "
¿Compiló numpy con mkl? ( http://software.intel.com/en-us/articles/intel-mkl/ ). Si está ejecutando 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:
[mkl]
library_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/lib/intel64
include_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/include
mkl_libs = mkl_intel_lp64,mkl_intel_thread,mkl_core
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 a Christoph Gohlke en el Laboratorio de Dinámica de Fluorescencia, UC Irvine.
Advertencia, en cualquier caso, hay muchos problemas de licencia, etc., que deben tenerse en cuenta, pero la página de inteligencia los explica. Nuevamente, me imagino que lo han considerado, pero si cumplen con los requisitos de licencia (lo 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 lo que otros piensan. De todos modos, excelente rigor y excelente pregunta. Gracias por publicarlo
He ejecutado woltan-benchmark . No hay diferencia entre C ++ y numpy en mi máquina:
¿Crees que mi enfoque es justo o hay algunos gastos indirectos innecesarios que puedo evitar?
Parece justo debido a que no hay diferencia en los resultados.
¿Esperaría que el resultado muestre una discrepancia tan grande entre el enfoque de c ++ y python? Ambos están utilizando objetos compartidos para sus cálculos.
No.
Como prefiero usar Python para mi programa, ¿qué podría hacer para aumentar el rendimiento cuando llamo a BLAS o LAPACK?
Asegúrese de que Numpy utilice una versión optimizada de las bibliotecas BLAS / LAPACK en su sistema.