Benchmarking (python vs. c++ usando BLAS) y (numpy)

| | | | | | | | | | | | | | | | | | | | | | | | | | |

Me gustaría escribir un programa que haga un uso extensivo de las funcionalidades de álgebra lineal de BLAS y LAPACK. Dado que el rendimiento es un problema, hice 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 matriz-matriz simple multiplicación. Los concursantes son:

  1. Numpy, haciendo uso únicamente de la funcionalidad de dot.
  2. Python, llamando a las funcionalidades de BLAS a través de un objeto compartido.
  3. C++, llamando a las funcionalidades de BLAS a través de un objeto compartido.

Escenario

Implementé una multiplicación matriz-matriz para diferentes dimensiones yo. 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) uno = c_float(1.0) cero = c_float(0.0) _blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref (n), byref(n), byref(uno), m1.ctypes.data_as(ctypes.c_void_p), byref(n), m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(cero) , 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_"); 

Yo 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 transcurrido con

doble CalcTime(timeval start, timeval end) { double factor = 1000000; return (((doble)fin.tv_sec) * factor + ((doble)fin.tv_usec) - (((doble)inicio.tv_sec) * factor + ((doble)inicio.tv_usec))) / factor; } 

Resultados

El resultado se muestra en el siguiente gráfico:

ingrese la descripción de la imagen aquí

Preguntas

  1. ¿Cree que mi enfoque es justo o hay algunos gastos generales innecesarios que puedo evitar?
  2. ¿Esperaría que el resultado mostrara una discrepancia tan grande entre el enfoque de C++ y Python? Ambos usan objetos compartidos para sus cálculos.
  3. Dado que preferiría usar python para mi programa, ¿qué podría 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^^)