2017-07-11 8 views
1

Ich möchte OpenMP nutzen, um meine Aufgabe parallel zu machen.Falsche Freigabe im OpenMP-Loop-Array-Zugriff

Ich muss die gleiche Menge an alle Elemente eines Arrays subtrahieren und das Ergebnis in einen anderen Vektor schreiben. Beide Arrays werden dynamisch mit malloc zugewiesen und das erste wird mit Werten aus einer Datei gefüllt. Jedes Element ist vom Typ uint64_t.

#pragma omp parallel for 
for (uint64_t i = 0; i < size; ++i) { 
    new_vec[i] = vec[i] - shift; 
} 

Wo shift ist der feste Wert I von jedem Element vec entfernen möchten. size ist die Länge von vec und new_vec, die ungefähr 200k ist.

Ich kompiliere den Code mit g++ -fopenmp auf Arch Linux. Ich bin auf einem Intel Core i7-6700HQ, und ich verwende 8 Threads. Die Laufzeit ist 5 bis 6 mal höher, wenn ich die OpenMP-Version verwende. Ich kann sehen, dass alle Kerne funktionieren, wenn ich die OpenMP-Version ausführe.

Ich denke, dass dies durch ein Problem der falschen Freigabe verursacht werden kann, aber ich kann es nicht finden.

+0

Ihre Speicherbandbreite ist begrenzt, da die Berechnung, die Sie versuchen, parallel zu machen, trivial ist und im Grunde nur Daten zwischen Speicherorten bewegt. Das Hinzufügen von Threads führt zu Cache-Miss/Thrash- und Pre-Fetch-Fehlern. Der Effekt davon ist, dass der Code langsamer läuft. Sehr ca. 1,5 Threads können den Speicherbus auf einem modernen PC sättigen. –

+0

@RichardCritten das ist nicht wahr. High-End-Prozessoren haben obere Bandbreitenbeschränkungen, die so ausgelegt sind, dass Sie Multithreading verwenden müssen, um sie zu sättigen. Schau dir den Link auf meiner Antwort an. –

+1

Wie messen Sie die Ausführungszeit? –

Antwort

1

Sie sollten anpassen, wie die Iterationen unter den Threads aufgeteilt werden. Mit schedule(static,chunk_size) können Sie dies tun.

Try chunk_size Werte ein Vielfaches von 64/sizeof (uint64_t) zu verwenden, um die genannten False Sharing zu vermeiden:

[ cache line n ][ cache line n+1 ] 
[ chuhk 0 ][ chunk 1 ][ chunk 2 ] 

Und so etwas wie dies zu erreichen:

[ cache line n ][ cache line n+1 ][ cache line n+2 ][...] 
[ chunk 0       ][ chunk 1    ] 

Sie sollten auch Ihre Vektoren zuweisen in der Weise, dass sie auf Cache-Zeilen ausgerichtet sind. Auf diese Weise stellen Sie sicher, dass der erste und die folgenden Chunks richtig ausgerichtet sind.

#define CACHE_LINE_SIZE sysconf(_SC_LEVEL1_DCACHE_LINESIZE) 
uint64_t *vec = aligned_alloc(CACHE_LINE_SIZE/*alignment*/, 200000 * sizeof(uint64_t)/*size*/); 

Ihr Problem ist wirklich ähnlich dem, was Stream Triad benchmark darstellt. Schauen Sie sich how to optimize that benchmark an und Sie können die Optimierungen Ihres Codes fast genau abbilden.

+1

Zu den ausführlichen Diskussionen über Stream gehört unter anderem die Wahl der Anzahl der Threads und deren Verteilung auf die Cores. Mehrere Threads pro Kern werden es fast sicher verlangsamen. Haben Sie die simd-Vektorisierung beibehalten, wenn Sie omp parallel setzen? – tim18

+0

Sie können sowohl Multithreading als auch Vektorisierung anfordern, indem Sie 'omp for simd' verwenden, eine Kombination aus der von Ihnen verwendeten und 'omp simd'. Überprüfen Sie die [Referenz für 'omp simd'] (https://software.intel.com/en-us/node/524530). Compiler können es möglicherweise ohne Hinweise vektorisieren, aber da Sie die Möglichkeit haben, es zu spezifizieren, ist es viel besser, die Klausel auch zu setzen. –

+2

Der Standard-Loop-Zeitplan für GCC ist 'static', daher gibt es genau 7 Cache-Zeilen von ~ 25000, die zwischen den Threads geteilt werden können, und bis zu 8 Iterationen mit falscher Freigabe von ~ 25000 pro Thread. Das Problem besteht wahrscheinlich darin, 'clock()' zu verwenden, um die Ausführungszeit unter Linux zu messen, und es gibt unzählige ähnliche Fragen. –