Optimierung und warum openmp ist viel langsamer als der sequentielle Art und Weise?
Ich bin ein Neuling in der Programmierung mit OpenMp. Ich schrieb ein einfaches c-Programm zu multiplizieren matrix mit einem Vektor. Leider, durch den Vergleich der Ausführung Zeit habe ich festgestellt, dass die OpenMP ist viel langsamer als die Sequentielle Art und Weise.
Hier ist mein code (Hier wird die matrix N*N-int, vector int N, Ergebnis N long long):
#pragma omp parallel for private(i,j) shared(matrix,vector,result,m_size)
for(i=0;i<m_size;i++)
{
for(j=0;j<m_size;j++)
{
result[i]+=matrix[i][j]*vector[j];
}
}
- Und dies ist der code für sequentielle Weise:
for (i=0;i<m_size;i++)
for(j=0;j<m_size;j++)
result[i] += matrix[i][j] * vector[j];
Als ich versuchte, diese beiden Implementierungen mit einer 999x999 matrix und einem 999-Vektor, der Zeitpunkt der Ausführung ist:
Sequentiell: 5439 ms
Parallel: 11120 ms
Kann ich wirklich nicht verstehen, warum OpenMP ist viel langsamer als der sequentielle Algorithmus (über 2 mal langsamer!) Wer kann mein problem lösen?
- Wie viele Kerne sind Sie mit OpenMP?
- Wie Messen Sie die execution time? Verwenden Sie die gefürchtete
clock() / CLOCKS_PER_SEC
Methode?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Weil, wenn OpenMP-verteilt die Arbeit unter den threads gibt es eine Menge von Verwaltung/Synchronisierung gehen, um zu gewährleisten, dass die Werte in Ihrem gemeinsamen matrix und Vektor sind nicht irgendwie beschädigt. Auch wenn Sie schreibgeschützt sind: Menschen sehen, dass leicht, dein compiler kann nicht.
Dinge ausprobieren, die für pädagogische Gründe:
0) Was passiert, wenn
matrix
undvector
sind nichtshared
?1) Parallelisieren der innere "j-loop" erste, halten Sie die äußere i-Schleife" serielle. Sehen, was passiert.
2) nicht erfassen die Summe in
result[i]
, sondern in eine variabletemp
und weisen Sie dessen Inhaltresult[i]
nur nach dem die innere Schleife beendet ist, um zu vermeiden wiederholte index-lookups. Vergessen Sie nicht, inittemp
auf 0, bevor die innere Schleife beginnt.Deinen code teilweise leidet unter dem sogenannten false-sharing, typisch für alle cache-kohärente Systeme. Kurz gesagt, viele Elemente der
result[]
array passen, in der gleichen cache-Zeile. Wenn threadi
schreibtresult[i]
als Ergebnis der+=
Betreiber, die cache-line holding, die Teil derresult[]
schmutzig wird. Das cache-Kohärenz-Protokoll dann ungültig macht, alle Kopien der cache-line in den anderen Kernen, und Sie müssen aktualisieren Sie Ihre Kopie von der oberen level-cache oder aus dem Hauptspeicher. Alsresult
ist ein array vonlong long
, dann eine cache line (64 bytes auf x86) besitzt 8 Elemente und nebenresult[i]
es gibt 7 andere array-Elemente in der gleichen cache-Zeile. Daher ist es möglich, dass zwei "benachbarten" Themen werden ständig kämpfen für den Besitz der cache-Zeile (unter der Annahme, dass jeder thread läuft auf einem separaten core).Mindern false-sharing in deinem Fall die einfachste Sache zu tun ist, um sicherzustellen, dass jeder thread bekommt eine iteration block, dessen Größe teilbar ist durch die Anzahl der Elemente in der cache-Zeile. Zum Beispiel können Sie die
schedule(static,something*8)
wosomething
sollte groß genug sein, so dass die iteration Speicherplatz wird nicht zersplittert in zu viele Teile, aber in der gleichen Zeit, sollte es klein genug sein, so dass jeder thread bekommt einen block. E. g. fürm_size
gleich 999 und 4 threads ein, die Sie anwenden würde, dieschedule(static,256)
- Klausel, um dieparallel for
konstruieren.Andere partielle Grund wird der code langsamer ausgeführt werden könnten, wenn OpenMP aktiviert ist, wird der compiler kann sich nur ungern gelten einige code-Optimierungen, die beim shared-Variablen werden zugeordnet. OpenMP bietet für die sogenannten relaxed memory-Modell, wo es erlaubt ist, dass der lokale Speicher der Blick auf eine gemeinsam genutzte variable in jedes Gewinde ist anders und die
flush
Konstrukt zur Verfügung gestellt, um die Synchronisierung der Ansichten. Aber Compiler in der Regel unter shared Variablen als implizitvolatile
wenn Sie nicht nachweisen können, dass andere threads keinen Zugriff auf desynchronised shared-Variablen. Ihr Fall ist einer von denen, daresult[i]
wird nur zugewiesen, und der Wert vonresult[i]
wird nie durch andere threads. Im seriellen Fall würde der compiler wahrscheinlich erstellen Sie eine temporäre variable, um das Ergebnis der inneren Schleife und würde nur die Zuordnung zuresult[i]
sobald die innere Schleife fertig ist. Im parallelen Fall könnte es entscheiden, dass dies eine temporäre desynchronised Blickresult[i]
in die anderen threads und damit entscheiden, die nicht die Anwendung der Optimierung. Nur für das Protokoll, GCC 4.7.1 mit-O3 -ftree-vectorize
hat die temporäre variable trick mit den beiden OpenMP aktiviert ist und nicht.Habe ich dies in Bezug auf Hristo Kommentar. Ich habe versucht, mit Zeitplan(statisch, 256). Für mich macht es nicht helfen, ändern Sie die Standard-chunck size. Vielleicht macht es sogar noch schlimmer. Ich druckte mir die thread-Anzahl und dem index mit und ohne Einstellung des Zeitplans und es ist klar, dass die OpenMP-schon wählt die thread-Indizes zu weit von einander, so dass false-sharing nicht scheinen ein Problem zu sein. Für mich ist dieser code gibt bereits einen guten Schub mit OpenMP.
loop_parallel
zweimal mit den gleichen Argumenten und Messen nur die zweite Aufruf.