Benchmarking (python vs. c++ mit BLAS) und (numpy)

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

Ich möchte ein Programm schreiben, das die Funktionalitäten der linearen Algebra von BLAS und LAPACK umfassend nutzt. Da Leistung ein Thema ist, habe ich ein Benchmarking durchgeführt und würde gerne wissen, ob der von mir gewählte Ansatz legitim ist.

Ich habe sozusagen drei Kandidaten und möchte ihre Leistung mit einer einfachen Matrix-Matrix testen Multiplikation. Die Teilnehmer sind:

  1. Numpy, das nur die Funktionalität von dot nutzt.
  2. Python, das die BLAS-Funktionalitäten über ein gemeinsames Objekt aufruft.
  3. C++, Aufruf der BLAS-Funktionalitäten durch ein gemeinsames Objekt.

Szenario

Ich habe eine Matrix-Matrix-Multiplikation für verschiedene Dimensionen implementiert ich. i läuft von 5 bis 500 mit einer Schrittweite von 5 und die Matrizen m1 und m2 sind wie folgt aufgebaut:

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

1. Numpy

Der verwendete Code sieht folgendermaßen aus:

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

2. Python, Aufruf von BLAS über ein gemeinsames Objekt

Mit der Funktion

_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(eins), m1.ctypes.data_as(ctypes.c_void_p), byref(n), m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(null) , r.ctypes.data_as(ctypes.c_void_p), byref(n)) 

der Testcode sieht so aus:

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++, Aufruf von BLAS über ein Shared Object

Jetzt ist der C++-Code natürlich etwas länger, also reduziere ich die Informationen auf ein Minimum.
Ich lade die Funktion mit

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

Ich messe die Zeit mit gettimeofday wie folgt:

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); 

wobei j eine Schleife ist, die 20 mal läuft. Ich berechne die vergangene Zeit mit

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

Ergebnisse

Das Ergebnis wird im Diagramm unten angezeigt:

Bildbeschreibung hier eingeben

Fragen

  1. Glauben Sie, dass mein Ansatz fair ist, oder gibt es einige unnötige Overheads, die ich machen kann vermeiden?
  2. Würden Sie erwarten, dass das Ergebnis eine so große Diskrepanz zwischen dem C++- und dem Python-Ansatz zeigen würde? Beide verwenden gemeinsam genutzte Objekte für ihre Berechnungen.
  3. Da ich lieber Python für mein Programm verwenden würde, was könnte ich tun, um die Leistung beim Aufrufen von BLAS- oder LAPACK-Routinen zu erhöhen?

Download

Der vollständige Benchmark kann hier heruntergeladen werden. (JF Sebastian hat diesen Link ermöglicht^^)