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:
- Numpy, das nur die Funktionalität von
dot
nutzt. - Python, das die BLAS-Funktionalitäten über ein gemeinsames Objekt aufruft.
- 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:
Fragen
- Glauben Sie, dass mein Ansatz fair ist, oder gibt es einige unnötige Overheads, die ich machen kann vermeiden?
- 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.
- 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^^)