Ich möchte ein Programm schreiben, das die linearen Algebra-Funktionen von BLAS und LAPACK in großem Umfang nutzt. Da Leistung ein Problem ist, habe ich ein Benchmarking durchgeführt und würde gerne wissen, ob der von mir verfolgte Ansatz legitim ist.
Ich habe sozusagen drei Teilnehmer und möchte ihre Leistung mit einer einfachen Matrix-Matrix-Multiplikation testen. Die Teilnehmer sind:
- Numpy, nutzt nur die Funktionalität von
dot
. - Python, der die BLAS-Funktionen über ein gemeinsam genutztes Objekt aufruft.
- C ++, Aufrufen der BLAS-Funktionen über ein gemeinsam genutztes Objekt.
Szenario
Ich habe eine Matrix-Matrix-Multiplikation für verschiedene Dimensionen implementiert i
. i
Läuft von 5 bis 500 mit einem Inkrement von 5 und den Matrizen m1
und m2
ist 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 ruft BLAS über ein gemeinsam genutztes Objekt auf
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(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))
Der Testcode sieht folgendermaßen 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 ++, BLAS über ein gemeinsam genutztes Objekt aufrufen
Jetzt ist der C ++ - Code natürlich etwas länger, so dass ich die Informationen auf ein Minimum reduziere.
Ich lade die Funktion mit
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Ich messe die Zeit folgendermaßen gettimeofday
:
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);
Wo j
läuft eine Schleife 20 Mal. Ich berechne die Zeit, die mit vergangen ist
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;
}
Ergebnisse
Das Ergebnis ist in der folgenden Darstellung dargestellt:
Fragen
- Halten Sie meinen Ansatz für fair oder gibt es unnötige Gemeinkosten, die ich vermeiden kann?
- Würden Sie erwarten, dass das Ergebnis eine so große Diskrepanz zwischen dem C ++ - und dem Python-Ansatz aufweist? Beide verwenden gemeinsam genutzte Objekte für ihre Berechnungen.
- Was kann ich tun, um die Leistung beim Aufrufen von BLAS- oder LAPACK-Routinen zu steigern, da ich lieber Python für mein Programm verwenden möchte?
Herunterladen
Der vollständige Benchmark kann hier heruntergeladen werden . (JF Sebastian hat diesen Link möglich gemacht ^^)
r
Matrix ist ungerecht. Ich löse gerade das "Problem" und veröffentliche die neuen Ergebnisse.np.ascontiguousarray()
(beachten Sie die Reihenfolge C vs. Fortran). 2. Stellen Sie sicher, dassnp.dot()
das gleiche verwendetlibblas.so
.m1
undm2
haben dasascontiguousarray
Flag alsTrue
. Und numpy verwendet dasselbe gemeinsame Objekt wie C. Was die Reihenfolge des Arrays betrifft: Derzeit interessiert mich das Ergebnis der Berechnung nicht, daher ist die Reihenfolge irrelevant.Antworten:
Ich habe Ihren Benchmark durchgeführt . Auf meinem Computer gibt es keinen Unterschied zwischen C ++ und numpy:
Es scheint fair zu sein, da es keinen Unterschied in den Ergebnissen gibt.
Nein.
Stellen Sie sicher, dass numpy eine optimierte Version der BLAS / LAPACK-Bibliotheken auf Ihrem System verwendet.
quelle
UPDATE (30.07.2014):
Ich habe den Benchmark für unser neues HPC erneut ausgeführt. Sowohl die Hardware als auch der Software-Stack haben sich gegenüber dem Setup in der ursprünglichen Antwort geändert.
Ich habe die Ergebnisse in eine Google-Tabelle eingefügt (enthält auch die Ergebnisse der ursprünglichen Antwort).
Hardware
Unser HPC verfügt über zwei verschiedene Knoten, einen mit Intel Sandy Bridge-CPUs und einen mit den neueren Ivy Bridge-CPUs:
Sandy (MKL, OpenBLAS, ATLAS):
Efeu (MKL, OpenBLAS, ATLAS):
Software
Der Software-Stack ist für beide Knoten der gleiche. Anstelle von GotoBLAS2 wird OpenBLAS verwendet, und es gibt auch ein ATLAS BLAS mit mehreren Threads , das auf 8 Threads (fest codiert) eingestellt ist.
Dot-Product-Benchmark
Der Benchmark-Code ist der gleiche wie unten. Für die neuen Maschinen habe ich jedoch auch den Benchmark für die Matrixgrößen 5000 und 8000 durchgeführt .
Die folgende Tabelle enthält die Benchmark-Ergebnisse der ursprünglichen Antwort (umbenannt in: MKL -> Nehalem MKL, Netlib Blas -> Nehalem Netlib BLAS usw.)
Single-Threaded-Leistung:
Leistung mit mehreren Threads (8 Threads):
Threads vs Matrixgröße (Ivy Bridge MKL) :
Benchmark Suite
Single-Threaded-Leistung:
Leistung mit mehreren Threads (8 Threads):
Fazit
Die neuen Benchmark-Ergebnisse ähneln denen in der ursprünglichen Antwort. OpenBLAS und MKL arbeiten mit Ausnahme des Eigenwerttests auf demselben Niveau . Der Eigenwerttest funktioniert unter OpenBLAS im Single-Threaded-Modus nur relativ gut . Im Multithread-Modus ist die Leistung schlechter.
Das Diagramm " Matrixgröße gegen Threads" zeigt auch, dass MKL und OpenBLAS zwar im Allgemeinen gut mit der Anzahl der Kerne / Threads skaliert werden, jedoch von der Größe der Matrix abhängen. Bei kleinen Matrizen verbessert das Hinzufügen weiterer Kerne die Leistung nicht wesentlich.
Es gibt auch eine Leistungssteigerung von ca. 30% von Sandy Bridge zu Ivy Bridge, was entweder auf eine höhere Taktrate (+ 0,8 GHz) und / oder eine bessere Architektur zurückzuführen sein kann.
Ursprüngliche Antwort (04.10.2011):
Vor einiger Zeit musste ich einige lineare Algebra-Berechnungen / Algorithmen optimieren, die in Python mit Numpy und BLAS geschrieben wurden, damit ich verschiedene Numpy / BLAS-Konfigurationen verglichen / getestet habe.
Speziell habe ich getestet:
Ich habe zwei verschiedene Benchmarks durchgeführt:
Hier sind meine Ergebnisse:
Maschinen
Linux (MKL, ATLAS, No-MKL, GotoBlas2):
Mac Book Pro (Accelerate Framework):
Mac Server (Accelerate Framework):
Dot Produkt Benchmark
Code :
Ergebnisse :
Benchmark Suite
Code :
Weitere Informationen zur Benchmark-Suite finden Sie hier .
Ergebnisse :
Installation
Die Installation von MKL beinhaltete die Installation der kompletten Intel Compiler Suite, was ziemlich einfach ist. Aufgrund einiger Fehler / Probleme war das Konfigurieren und Kompilieren von numpy mit MKL-Unterstützung jedoch ein bisschen mühsam.
GotoBlas2 ist ein kleines Paket, das einfach als gemeinsam genutzte Bibliothek kompiliert werden kann. Aufgrund eines Fehlers müssen Sie die gemeinsam genutzte Bibliothek nach dem Erstellen neu erstellen, um sie mit numpy verwenden zu können.
Zusätzlich zu diesem Gebäude funktionierte es für mehrere Zielplattformen aus irgendeinem Grund nicht. So hatte ich eine erstellen .so - Datei für jede Plattform , für die ich möchte eine optimierte haben libgoto2.so Datei.
Wenn Sie numpy aus dem Ubuntu-Repository installieren, wird numpy automatisch installiert und für die Verwendung von ATLAS konfiguriert . Die Installation von ATLAS von der Quelle kann einige Zeit dauern und erfordert einige zusätzliche Schritte (fortran usw.).
Wenn Sie installieren numpy auf einem Mac OS X - Rechner mit Fink oder Mac Ports wird es entweder configure numpy verwenden ATLAS oder Apple - Framework beschleunigen . Sie können dies überprüfen, indem Sie entweder ldd für die Datei numpy.core._dotblas ausführen oder numpy.show_config () aufrufen .
Schlussfolgerungen
MKL schneidet am besten ab, dicht gefolgt von GotoBlas2 .
Im Eigenwerttest schneidet GotoBlas2 überraschend schlechter ab als erwartet. Ich bin mir nicht sicher, warum dies der Fall ist.
Das Accelerate Framework von Apple bietet eine hervorragende Leistung, insbesondere im Single-Threaded-Modus (im Vergleich zu den anderen BLAS-Implementierungen).
Sowohl GotoBlas2 als auch MKL skalieren sehr gut mit der Anzahl der Threads. Wenn Sie sich also mit großen Matrizen befassen müssen, die auf mehreren Threads ausgeführt werden, hilft dies sehr.
Verwenden Sie auf keinen Fall die Standardimplementierung von netlib blas , da diese für ernsthafte Rechenarbeiten viel zu langsam ist.
Auf unserem Cluster habe ich auch AMDs ACML installiert und die Leistung war ähnlich wie bei MKL und GotoBlas2 . Ich habe keine harten Zahlen.
Ich persönlich würde empfehlen, GotoBlas2 zu verwenden, da es einfacher zu installieren und kostenlos ist.
Wenn Sie in C ++ / C codieren möchten, schauen Sie sich auch Eigen3 an, das in einigen Fällen MKL / GotoBlas2 übertreffen soll und auch recht einfach zu bedienen ist.
quelle
Hier ist ein weiterer Benchmark (unter Linux einfach eingeben
make
): http://dl.dropbox.com/u/5453551/blas_call_benchmark.ziphttp://dl.dropbox.com/u/5453551/blas_call_benchmark.png
Ich sehe im Wesentlichen keinen Unterschied zwischen den verschiedenen Methoden für große Matrizen, zwischen Numpy, Ctypes und Fortran. (Fortran anstelle von C ++ --- und wenn dies wichtig ist, ist Ihr Benchmark wahrscheinlich gebrochen.)
IhreVielleicht weist Ihr Benchmark auch andere Fehler auf, z. B. den Vergleich zwischen verschiedenen BLAS-Bibliotheken oder verschiedenen BLAS-Einstellungen wie der Anzahl der Threads oder zwischen Echtzeit und CPU-Zeit?CalcTime
Funktion in C ++ scheint einen Vorzeichenfehler zu haben.... + ((double)start.tv_usec))
sollte stattdessen sein... - ((double)start.tv_usec))
.BEARBEITEN : Die geschweiften Klammern in der
CalcTime
Funktion konnten nicht gezählt werden - es ist in Ordnung.Als Richtlinie: Wenn Sie einen Benchmark durchführen, veröffentlichen Sie bitte immer den gesamten Code irgendwo. Das Kommentieren von Benchmarks, insbesondere wenn sie überraschend sind, ohne den vollständigen Code zu haben, ist normalerweise nicht produktiv.
Um herauszufinden, mit welchem BLAS Numpy verknüpft ist, gehen Sie wie folgt vor:
UPDATE : Wenn Sie numpy.core._dotblas nicht importieren können, verwendet Ihr Numpy seine interne Fallback-Kopie von BLAS, die langsamer ist und nicht für das Performance-Computing vorgesehen ist! Die Antwort von @Woltan unten zeigt, dass dies die Erklärung für den Unterschied ist, den er / sie in Numpy vs. Ctypes + BLAS sieht.
Um die Situation zu beheben, benötigen Sie entweder ATLAS oder MKL. Überprüfen Sie diese Anweisungen: http://scipy.org/Installing_SciPy/Linux Die meisten Linux-Distributionen werden mit ATLAS ausgeliefert. Die beste Option ist daher die Installation des
libatlas-dev
Pakets (Name kann variieren). .quelle
import numpy.core._dotblas
. Was könnte hier das Problem sein? Ich werde versuchen, meinen Benchmark zu bereinigen und ein Makefile zu schreiben, damit andere es testen können.otool -L
anstelle vonldd
unter LinuxAngesichts der Genauigkeit, die Sie bei Ihrer Analyse gezeigt haben, bin ich von den bisherigen Ergebnissen überrascht. Ich habe dies als "Antwort" angegeben, aber nur, weil es zu lang für einen Kommentar ist und eine Möglichkeit bietet (obwohl ich davon ausgehe, dass Sie darüber nachgedacht haben).
Ich hätte gedacht, dass der Numpy / Python-Ansatz für eine Matrix mit angemessener Komplexität nicht viel Overhead bedeuten würde, da mit zunehmender Komplexität der Anteil, an dem Python beteiligt ist, gering sein sollte. Ich bin mehr an den Ergebnissen auf der rechten Seite des Diagramms interessiert, aber die dort gezeigten Abweichungen um Größenordnungen wären störend.
Ich frage mich, ob Sie die besten Algorithmen verwenden, die Numpy nutzen kann. Aus der Kompilierungsanleitung für Linux:
"Build FFTW (3.1.2): SciPy-Versionen> = 0.7 und Numpy> = 1.2: Aufgrund von Lizenz-, Konfigurations- und Wartungsproblemen wurde die Unterstützung für FFTW in Versionen von SciPy> = 0.7 und NumPy> = 1.2 entfernt. Verwendet stattdessen jetzt Eine integrierte Version von fftpack. Es gibt verschiedene Möglichkeiten, die Geschwindigkeit von FFTW zu nutzen, falls dies für Ihre Analyse erforderlich ist. Downgrade auf eine Numpy / Scipy-Version mit Unterstützung. Installieren oder erstellen Sie Ihren eigenen FFTW-Wrapper. Siehe http: //developer.berlios.de/projects/pyfftw/ als nicht empfohlenes Beispiel. "
Hast du numpy mit mkl kompiliert? ( http://software.intel.com/en-us/articles/intel-mkl/ ). Wenn Sie unter Linux arbeiten, finden Sie hier die Anweisungen zum Kompilieren von numpy mit mkl: http://www.scipy.org/Installing_SciPy/Linux#head-7ce43956a69ec51c6f2cedd894a4715d5bfff974 (trotz URL). Der Schlüsselteil ist:
Wenn Sie unter Windows arbeiten, können Sie eine kompilierte Binärdatei mit mkl (und auch pyfftw und viele andere verwandte Algorithmen) unter folgender Adresse abrufen: http://www.lfd.uci.edu/~gohlke/pythonlibs/ mit a Dank an Christoph Gohlke vom Labor für Fluoreszenzdynamik, UC Irvine.
Vorsichtsmaßnahme In beiden Fällen sind viele Lizenzprobleme usw. zu beachten, die jedoch auf der Intel-Seite erläutert werden. Ich stelle mir wieder vor, Sie haben dies in Betracht gezogen, aber wenn Sie die Lizenzanforderungen erfüllen (was unter Linux sehr einfach ist), würde dies den Numpy-Teil im Vergleich zur Verwendung eines einfachen automatischen Builds ohne FFTW erheblich beschleunigen. Ich werde interessiert sein, diesem Thread zu folgen und zu sehen, was andere denken. Unabhängig davon, ausgezeichnete Genauigkeit und ausgezeichnete Frage. Danke, dass du es gepostet hast.
quelle