2016-07-28 16 views
2

Ich arbeite mit OpenMP, um einen Algorithmus mit einer nahezu linearen Beschleunigung zu erhalten. Leider ist mir aufgefallen, dass ich die gewünschte Beschleunigung nicht erreichen konnte.Keine Beschleunigung mit OpenMP

Also, um den Fehler in meinem Code zu verstehen, schrieb ich einen anderen Code, einen einfachen, nur um zu überprüfen, dass die Beschleunigung im Prinzip auf meiner Hardware erhältlich war.

Dies ist das Spielzeug Beispiel schrieb ich:

#include <omp.h> 
#include <cmath> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include <string.h> 
#include <cstdlib> 
#include <fstream> 
#include <sstream> 
#include <iomanip> 
#include <iostream> 
#include <stdexcept> 
#include <algorithm> 
#include "mkl.h" 

int main() { 
     int number_of_threads = 1; 
     int n = 600; 
     int m = 50; 
     int N = n/number_of_threads; 
     int time_limit = 600; 
     double total_clock = omp_get_wtime(); 
     int time_flag = 0; 

     #pragma omp parallel num_threads(number_of_threads) 
     { 
      int thread_id = omp_get_thread_num(); 
      int iteration_number_local = 0; 
      double *C = new double[n]; std::fill(C, C+n, 3.0); 
      double *D = new double[n]; std::fill(D, D+n, 3.0); 
      double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 

      while (time_flag == 0){ 
       for (int i = 0; i < N; i++)      
        for(int z = 0; z < m; z++) 
         for(int x = 0; x < n; x++) 
          for(int c = 0; c < n; c++){ 
           CD[c] = C[z]*D[x]; 
           C[z] = CD[c] + D[x]; 
          } 
       iteration_number_local++; 
       if ((omp_get_wtime() - total_clock) >= time_limit) 
        time_flag = 1; 
      } 
     #pragma omp critical 
     std::cout<<"I am "<<thread_id<<" and I got" <<iteration_number_local<<"iterations."<<std::endl; 
     } 
    } 

ich noch einmal hervorheben, dass dieser Code nur ein Spielzeug-Beispiel wollen zu versuchen, die Speedup zu sehen: der erste für Zyklus kürzer wird, wenn die Anzahl von parallelen Threads erhöht (seit N abnimmt).

Wenn ich jedoch von 1 bis 2-4 Threads gehe, verdoppelt sich die Anzahl der Iterationen wie erwartet; Dies ist jedoch nicht der Fall, wenn ich 8-10-20 Threads verwende: Die Anzahl der Iterationen steigt nicht linear mit der Anzahl der Threads.

Könnten Sie mir bitte dabei helfen? Ist der Code korrekt? Sollte ich eine nahezu lineare Beschleunigung erwarten?

Ergebnisse

Ausführen des Codes oben habe ich die folgenden Ergebnisse erhielt.

1 Thread: 23 Iterationen.

20 Threads: 397-401 Iterationen pro Thread (statt 420-460).

+0

Mit welcher Hardware arbeiten Sie? Bitte geben Sie spezifische Angaben zu Prozessor (en) und Speicher an. Welche Compiler-Version und welche Optionen und welches Betriebssystem? Wie viele Iterationen beobachten Sie? – Zulan

+0

Problematische Aspekte in Ihrer Messung: 'CD' wird nie benutzt, so dass der Compiler alles, was Sie teuer erwarten, einfach optimieren kann. Sie sollten mindestens "iteration_number_local" ausgeben (verwenden Sie 'pragma omp kritisch '). – Zulan

+0

Ich führe den Code auf einer Hardware mit zwei 10-Core Intel Xeon-E5 (so habe ich 20 Kerne insgesamt) mit 256 GB RAM. Das Betriebssystem ist Linux. Ich weiß nicht über den Compiler: Ich lade ein Modul namens "gsl 1.15", während die cmake einen Compiler namens "ICC" aufrufen. Ich denke, das ist nicht das, was Sie gefragt haben, bitte klären Sie mich besser. Ich mache einige schnelle Simulationen mit n = 1000, m = 200. Mit 1 Thread bekomme ich 3 Iterationen in 120 Sekunden. Mit 2 Threads bekomme ich 5 Iterationen pro Thread (statt 6). Bei 20 Threads bekomme ich zwischen 40 und 44 Iterationen pro Thread (statt 60!). – Mobius88

Antwort

0

Sie machen eine Deklaration innerhalb der parallelen Region, was bedeutet, dass Sie die Memorie zuweisen und sie number_of_threads mal füllen. Stattdessen Ich empfehle Ihnen:

double *C = new double[n]; std::fill(C, C+n, 3.0); 
double *D = new double[n]; std::fill(D, D+n, 3.0); 
double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 
#pragma omp parallel firstprivate(C,D,CD) num_threads(number_of_threads) 
    { 
     int thread_id = omp_get_thread_num(); 
     int iteration_number_local = 0; 
    } 

Ihre Hardware eine begrenzte Menge von Threads haben, die von der Anzahl der Kern Ihres Prozessors abhängt. Sie können 2 oder 4 Kern haben.

Eine parallele Region beschleunigt nicht Ihren Code. Mit offenen OpenMP sollten Sie #omp parallel verwenden, um für Schleife zu beschleunigen oder

#pragma omp parallel 
{ 
    #pragma omp for 
    { 
    } 
} 

diese Schreibweise entspricht #pragma omp parallel zu. Es verwendet mehrere Threads (abhängig von Ihrer Hardware), um die for-Schleife schneller auszuführen. vorsichtig sein

#pragma omp parallel 
{ 
    for 
    { 
    } 
} 

wird für jeden Thread das gesamte for-Schleife machen, die Ihr Programm nicht beschleunigen wird nach oben.

+0

Ich bin mir nicht sicher, dass Sie Recht haben. Das Konstrukt #pragma omp parallel ermöglicht die Anzahl der Threads, die ich brauche, um alle Befehle innerhalb des Blocks unabhängig auszuführen. So wird jeder Thread die verschachtelte-for in den parallelen Block ausführen. Sie können sehen, dass der erste for-Zyklus kürzer wird, wenn die Anzahl der Threads zunimmt, also sollte es eine lineare Beschleunigung geben. Ich führe diesen Code auf einem Cluster-Computer mit 20 Kernen pro Knoten. – Mobius88

+0

Natürlich wird der erste for-cycle kürzer, aber Sie werden es number_of_threads mal machen. Schließlich werden Sie n/number_of_threads * number_of_threads Operationen vornehmen. –

+0

#pragma omp parallel { #pragma omp für { } } und #pragma omp parallel { für { } } sind nicht die gleiche Anweisung –

0

sollten Sie

versuchen
#pragma omp parallel num_threads(number_of_threads) 
    { 
     int thread_id = omp_get_thread_num(); 
     int iteration_number_local = 0; 
     double *C = new double[n]; std::fill(C, C+n, 3.0); 
     double *D = new double[n]; std::fill(D, D+n, 3.0); 
     double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 

     while (time_flag == 0){ 
      #pragma omp for 
      for (int i = 0; i < N; i++)      
       for(int z = 0; z < m; z++) 
        for(int x = 0; x < n; x++) 
         for(int c = 0; c < n; c++) 
          CD[c] = C[z]*D[x]; 
      iteration_number_local++; 
      if ((omp_get_wtime() - total_clock) >= time_limit) 
       time_flag = 1; 
     } 
     if(thread_id == 0) 
     iteration_number = iteration_number_local; 
    } 
    std::cout<<"Iterations= "<<iteration_number<<std::endl; 
} 
+2

Sie sollten Ihre vorherige Antwort bearbeiten, anstatt eine neue zu schreiben. –

1

Ihre Messmethodik falsch ist. Besonders für kleine Anzahl von Iterationen.

1 Thread: 3 Iterationen.

3 berichtet Iterationen tatsächlich bedeutet, dass 2 Iterationen in weniger als 120 s beendet. Der dritte dauerte länger. Die Zeit von 1 Iteration liegt zwischen 40 und 60 s.

2 Threads: 5 Iterationen pro Thread (statt 6).

4 Iterationen in weniger als 120 s abgeschlossen. Die Zeit von 1 Iteration liegt zwischen 24 und 30 s.

20 Threads: 40-44 Iterationen pro Thread (statt 60).

40 Iterationen in weniger als 120 s abgeschlossen. Die Zeit von 1 Iteration liegt zwischen 2,9 und 3 s.

Wie Sie sehen können, widersprechen Ihre Ergebnisse tatsächlich der linearen Beschleunigung nicht.

Es wäre viel einfacher und genauer, einfach eine einzige äußere Schleife auszuführen und zu messen, und Sie werden wahrscheinlich eine fast perfekte lineare Beschleunigung sehen.

Einige Gründe (nicht erschöpfende), warum Sie nicht linearen Speedup sehen sind:

  1. Speicher gebunden Leistung. Nicht der Fall in Ihrem Spielzeugbeispiel mit n = 1000. Allgemeiner gesprochen: Konkurrenz für eine gemeinsam genutzte Ressource (Hauptspeicher, Caches, I/O).
  2. Synchronisation zwischen Threads (z. B. kritische Abschnitte). Nicht der Fall in Ihrem Spielzeugbeispiel.
  3. Lastungleichgewicht zwischen den Gewinden. Nicht der Fall in Ihrem Spielzeugbeispiel.
  4. Im Turbo-Modus werden niedrigere Frequenzen verwendet, wenn alle Kerne verwendet werden. Dies kann in Ihrem Spielzeugbeispiel passieren.

Von Ihrem Spielzeug Beispiel würde ich sagen, dass Ihr Ansatz OpenMP kann durch eine bessere Verwendung der hohen Abstraktionen verbessert werden, zum Beispiel for.

Allgemeinere Hinweise wären für dieses Format zu umfangreich und erfordern spezifischere Informationen zum Nicht-Spielzeug-Beispiel.

+0

Ich stimme dieser Antwort zu, also laufe ich längere Simulationen. Ich führe einen Code wie den obigen aus, indem ich einen weiteren Befehl in die verschachtelte Schleife einbeziehe: C [z] = CD [c] + D [x]; um CD zu verwenden, wie Sie vorgeschlagen haben. Durch Setzen von n = 600 und m = 50 erhielt ich 23 Iter mit einem Thread in 600 Sekunden und 400 Iterationen pro Thread mit 20 Threads in 600 Sekunden. Es ist nicht die erwartete Beschleunigung. Habe ich recht? – Mobius88

+0

399/22 ist fast 20 x Beschleunigung. Nahe genug, um als eine nahezu lineare Beschleunigung in einer realen Anwendung vollkommen akzeptabel zu sein. Auch leicht erklärbar durch Turbo-Modus oder auch nur Varianz. – Zulan

+0

Da die Arrays klein genug sind, um in den L1-Cache zu passen, ist es wahrscheinlich die Frequenzskalierung. –