2015-01-12 13 views
5

Ich habe diese einfache Member-Funktion aus einem größeren 2D-Programm extrahiert, es ist nur eine for-Schleife Zugriff von drei verschiedenen Arrays und eine mathematische Operation (1D Faltung). Ich habe mit der Verwendung von OpenMP teste diese spezielle Funktion schneller zu machen:Warum ist diese For-Schleife mit OpenMP nicht schneller?

void Image::convolve_lines() 
{ 
    const int *ptr0 = tmp_bufs[0]; 
    const int *ptr1 = tmp_bufs[1]; 
    const int *ptr2 = tmp_bufs[2]; 
    const int width = Width; 
#pragma omp parallel for 
    for (int x = 0; x < width; ++x) 
    { 
    const int sum = 0 
     + 1 * ptr0[x] 
     + 2 * ptr1[x] 
     + 1 * ptr2[x]; 
    output[x] = sum; 
    } 
} 

Wenn ich gcc 4.7 auf debian/wheezy amd64 das Gesamtprogramm viel langsamer auf einer 8 CPUs Maschine führt. Wenn ich gcc 4.9 auf einem debian/jessie amd64 (nur 4 CPUs auf diesem Rechner) benutze, läuft das Gesamtprogramm mit sehr wenig Unterschied ab.

time Verwendung zum Vergleich: Single-Core-Lauf:

$ ./test black.pgm out.pgm 94.28s user 6.20s system 84% cpu 1:58.56 total 

Multi-Core-Lauf:

$ ./test black.pgm out.pgm 400.49s user 6.73s system 344% cpu 1:58.31 total 

Wo:

$ head -3 black.pgm 
P5 
65536 65536 
255 

So Width-65536 während der Ausführung eingestellt ist.

Wenn diese Angelegenheit, ich bin mit cmake zum Kompilieren vor:

add_executable(test test.cxx) 
set_target_properties(test PROPERTIES COMPILE_FLAGS "-fopenmp" LINK_FLAGS "-fopenmp") 

Und CMAKE_BUILD_TYPE ist eingestellt auf:

CMAKE_BUILD_TYPE:STRING=Release 

die -O3 -DNDEBUG

Meine Frage impliziert, warum ist diese for Schleife nicht schneller mit Multi-Core? Es gibt keine Überlappung im Array, openmp sollte den Speicher gleichmäßig teilen. Ich sehe nicht, wo der Engpass herkommt?

EDIT: wie es kommentiert wurde, änderte ich meine Eingabedatei in:

$ head -3 black2.pgm 
P5 
33554432 128 
255 

So wird Width nun auf 33554432 während der Ausführung (sollte genug betrachtet werden). Nun ist das Timing zeigt:

Single-Core-Lauf:

$ ./test ./black2.pgm out.pgm 100.55s user 5.77s system 83% cpu 2:06.86 total 

Multi-Core-Lauf (aus irgendeinem Grunde cpu% war immer unter 100%, die keine Fäden überhaupt anzeigen würden):

$ ./test ./black2.pgm out.pgm 117.94s user 7.94s system 98% cpu 2:07.63 total 
+2

im Allgemeinen, falsche Freigabe/Sperre Konkurrenz. Wie groß ist die Breite? – sehe

+0

@sehe Entschuldigung vergessen zu erwähnen, dass. – malat

+0

Wie haben Sie das getestet? Ich bezweifle, dass die einzelne 64k-Schleife, die Sie gegeben haben, so viel Zeit in Anspruch nimmt. – ElderBug

Antwort

2

Ich habe einige allgemeine Kommentare:

1. Bevor Sie Ihren Code optimieren, stellen Sie sicher, dass die Daten 16 Byte ausgerichtet sind. Dies ist extrem wichtig für jede Optimierung, die man anwenden möchte. Und wenn die Daten in 3 Teile aufgeteilt sind, ist es besser, einige Dummy-Elemente hinzuzufügen, damit die Startadressen der 3 Teile alle 16 Byte ausgerichtet sind. Auf diese Weise kann die CPU Ihre Daten problemlos in Cache-Zeilen laden.

2. Stellen Sie sicher, dass die einfache Funktion vektorisiert ist, bevor Sie openMP implementieren. Die meisten Fälle, in denen AVX/SSE-Befehlssätze verwendet werden, sollten Ihnen eine anständige 2- bis 8-fache Verbesserung des einzelnen Threads ermöglichen.Und es ist sehr einfach für Ihren Fall: Erstellen Sie ein konstantes mm256-Register und legen Sie es mit dem Wert 2 fest und laden Sie 8 ganze Zahlen in drei mm256-Register. Mit dem Haswell-Prozessor können eine Addition und eine Multiplikation zusammen durchgeführt werden. Theoretisch sollte sich die Schleife um den Faktor 12 beschleunigen, wenn die AVX-Pipeline gefüllt werden kann!

3. Manchmal kann die Parallelisierung die Leistung beeinträchtigen: Moderne CPU benötigt mehrere hundert bis tausende Taktzyklen zum Aufwärmen, zum Eintreten in Hochleistungszustände und zur Erhöhung der Frequenz. Wenn die Task nicht groß genug ist, ist es sehr wahrscheinlich, dass die Task ausgeführt wird, bevor sich die CPU aufwärmt, und es kann keine Geschwindigkeitsverstärkung durch paralleles Verhalten erreicht werden. Und vergessen Sie nicht, dass openMP auch Overhead hat: Thread-Erstellung, Synchronisation und Löschung. Ein anderer Fall ist eine schlechte Speicherverwaltung. Datenzugriffe sind so verstreut, dass alle CPU-Kerne im Leerlauf sind und auf Daten aus dem RAM warten.

Mein Vorschlag:

Vielleicht möchten Sie Intel MKL, um zu versuchen, neu zu erfinden das Rad nicht. Die Bibliothek ist extrem optimiert und es wird kein Taktzyklus verschwendet. Man kann sich mit der seriellen Bibliothek oder der parallelen Version verbinden, ein Geschwindigkeitsschub ist garantiert, wenn es parallel geht.

Verwandte Themen