Benchmarking (python vs. c++ met BLAS) en (numpy)

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

Ik zou graag een programma willen schrijven dat uitgebreid gebruik maakt van BLAS en LAPACK lineaire algebra functionaliteiten. Omdat prestaties een probleem zijn, heb ik wat benchmarking gedaan en zou graag willen weten of de benadering die ik heb gevolgd legitiem is.

Ik heb, om zo te zeggen, drie deelnemers en wil hun prestaties testen met een eenvoudige matrix-matrix vermenigvuldiging. De deelnemers zijn:

  1. Numpy, die alleen gebruik maakt van de functionaliteit van dot.
  2. Python, die de BLAS-functionaliteit aanroept via een gedeeld object.
  3. C++, roept de BLAS-functionaliteiten aan via een gedeeld object.

Scenario

Ik implementeerde een matrix-matrix-vermenigvuldiging voor verschillende dimensies ik. i loopt van 5 tot 500 met een toename van 5 en de matrices m1 en m2 zijn als volgt ingesteld:

m1 = numpy.random.rand(i,i).astype(numpy.float32) m2 = numpy.random.rand(i,i).astype(numpy.float32) 

1. Numpy

De gebruikte code ziet er als volgt uit:

tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1 , m2") rNumpy.append((i, tNumpy.repeat(20, 1))) 

2. Python, BLAS aanroepend via een gedeeld object

Met de functie

_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(nul) , r.ctypes.data_as(ctypes.c_void_p), byref(n)) 

de testcode ziet er als volgt uit:

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++, aanroepen van BLAS via een gedeeld object

Nu is de c++-code natuurlijk iets langer, dus ik beperk de informatie tot een minimum.
Ik laad de functie met

void* handle = dlopen("libblas.so", RTLD_LAZY); void* Func = dlsym(handle, "sgemm_"); 

Ik meet de tijd met gettimeofday als volgt:

gettimeofday(&start, NULL); f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &nul, Return, &dim); gettimeofday(&end, NULL); dTimes[j] = CalcTime(begin, einde); 

waarbij j een lus is die 20 keer wordt uitgevoerd. Ik bereken de verstreken tijd met

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

Resultaten

Het resultaat wordt weergegeven in de onderstaande grafiek:

voer hier afbeeldingsbeschrijving in

Vragen

  1. Vind je mijn aanpak eerlijk, of zijn er onnodige overheadkosten die ik kan vermijden?
  2. Zou je verwachten dat het resultaat zo'n enorme discrepantie zou vertonen tussen de c++- en python-aanpak? Beide gebruiken gedeelde objecten voor hun berekeningen.
  3. Aangezien ik liever Python voor mijn programma gebruik, wat kan ik dan doen om de prestaties te verbeteren bij het aanroepen van BLAS- of LAPACK-routines?

Download

De volledige benchmark kan hier worden gedownload. (JF Sebastian heeft die link mogelijk gemaakt^^)