MPI - for Schleife aufteilen



  • Ich habe das Gefühl, dass du etwas falsch machst. Ohne Code lässt sich dies aber leider nicht bestätigen.


  • Mod

    Hier ein Beispiel. Nicht sehr elegant, dafür einfach. Ich hätte auch boost::mpi benutzen können, aber ehrlich gesagt habe ich mit den normalen MPI Funktionen mehr Erfahrung.

    #include <mpi.h>
    #include <iostream>
    #include <vector>
    using namespace std;
    
    double stuetzstelle(unsigned i)
    {
      return i*i-1.; // Lange, schwere Rechnung ;-)
    }
    
    int main(int argc, char* argv[])
    {
      int  numtasks, rank, rc; 
    
      // Ein bisschen Standardbuchhaltung:
      rc = MPI_Init(&argc,&argv);
      if (rc != MPI_SUCCESS) {
        cerr<<"Fehler beim Initialisieren der MPI-Umgebung"<<endl;
        MPI_Abort(MPI_COMM_WORLD, rc);
      }
    
      MPI_Comm_size(MPI_COMM_WORLD,&numtasks);
      MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    
      cout<<"Prozess "<< rank << " von " << numtasks<< " ist bereit.\n";
    
      // Frage nach der Aufgabe...
      unsigned int stellen;
      if (rank==0)
        {
          cout<<"Wieviele Stützstellen sollen berechnet werden?\n";
          cin>>stellen;
        }
      // ...und verteile diese
      MPI_Bcast(&stellen, 1, MPI_INT, 0, MPI_COMM_WORLD);
    
      // Die Prozesse schnappen sich ihren Block...
      unsigned int anfang=rank*(stellen/numtasks);
      unsigned int ende;
      if (rank!=numtasks-1)
        {
          ende=(rank+1)*(stellen/numtasks);
        }
      else
        {
          ende=stellen;
        }
      cout<<"Prozess "<<rank<<" berechnet von "<< anfang<< " bis "<<ende-1<<".\n";
    
      // ...und berechnen ihn.
      vector<double> ergebnisse(stellen);
      for(unsigned int i=anfang; i<ende; ++i)
        {
          ergebnisse[i]=stuetzstelle(i);
        }
    
      // Dann alles zusammentragen:
      MPI_Gather(&ergebnisse[anfang], stellen/numtasks, MPI_DOUBLE, &ergebnisse[0], stellen/numtasks, MPI_DOUBLE, numtasks-1, MPI_COMM_WORLD);
    
      if (rank==numtasks-1)
        {
          cout<<"Prozess "<<rank<<" hat nun folgende Daten:\n";
          for(unsigned i=0; i<stellen; ++i)
            cout<<"ergebnisse["<<i<<"]: "<<ergebnisse[i]<<'\n';
        }
    
     MPI_Finalize();
    }
    

    Das sind aber absolute Grundlagen, das kannste in jedem Tutorial auf der ersten Seite lesen. Manche Tutorials fangen sogar mit fortgeschritteneren Sachen an!



  • Wow, vielen Dank, du hast mir sehr geholfen! Super Beispiel!
    Hab meinen Code schon angepasst, aber kann ihn im Moment leider nicht testen, da der Cluster gerad aus irgend einem Grund wieder nicht funktioniert...aber ich geb noch Rueckmeldung!


  • Mod

    kaesekuchen86 schrieb:

    Wow, vielen Dank, du hast mir sehr geholfen! Super Beispiel!
    Hab meinen Code schon angepasst, aber kann ihn im Moment leider nicht testen, da der Cluster gerad aus irgend einem Grund wieder nicht funktioniert...aber ich geb noch Rueckmeldung!

    MPI kann man auch auf ganz normalen Rechnern installieren. Hilft sehr beim Testen.



  • kaesekuchen86 schrieb:

    Ich würde daher die Berechnungen gerne aufteilen, so dass wenn z.B. die ursprüngliche for Schleife von sagen wir 0 bis 99 läuft, jeder PC den gleichen Anteil der Berechnungen übernimmt, also bei 2 PCs der erste die Elemente 0 bis 49 und der zweite die Elemente 50 bis 99 berechnet.

    Das übliche Verfahren zur Parallelisierung von SciTech Software besteht darin, auf dem Knoten mittels OpenMP und zwischen den Knoten mittels MPI zu parallelisieren. Was sinnvoller ist, läßt sich nur nach einer konkreten Beurteilung eines bestimmten Programmcodes sagen.

    Wenn das Problem wirklich groß ist, nutzt man beides.



  • Also ich habs jetzt geschafft mein Programm umzuschreiben, es läuft auch daheim auf meinem PC (Hab MPI installiert, danke für den Hinweis!).
    Werde das ganze die Tage noch auf dem Cluster in der Uni testen. Freue mich schon, endlich nicht mehr mehrere Stunden auf Ergebnisse warten zu müssen 🙂

    Vielen Dank nochmal für die Hilfe!



  • kaesekuchen86 schrieb:

    Freue mich schon, endlich nicht mehr mehrere Stunden auf Ergebnisse warten zu müssen 🙂

    Glücklicher, bei mir dauert das trotz MPI+OpenMP immer noch Tage.



  • Hallo, ich bins nochmal....
    Ich muss jetzt ein 2 dimenionales Array der Größe n x n aufteilen und scheiter gerad an der Umsetzung. Jeder Prozessor berechnet n/size Reihen des Arrays (size=Anzahl der Prozessoren) und soll sie danach an alle anderen schicken.

    Ich hab es mit

    MPI_Allgather (&array[startgrid][0],n*n/size,MPI_DOUBLE_COMPLEX,&array[startgrid][0],n*n/size,MPI_DOUBLE_COMPLEX,MPI_COMM_WORLD);
    

    und mit

    MPI_Allgather (&array[startgrid][0],n*n/size,MPI_DOUBLE_COMPLEX,&array[0][0],n*n/size,MPI_DOUBLE_COMPLEX,MPI_COMM_WORLD);
    

    und erhalte jedesmal einen Speicherzugriffsfehler. Bei der ersten Methode direkt beim Aufruf von MPI_Allgather, bei der zweiten Methode erst beim Aufruf von MPI_Finalize.
    Bei dem Array handelt es sich um einen Vector aus der STL.

    Was machen ich falsch???


  • Mod

    Du hast einen vector von vectoren nehme ich an? Denk daran, dass da die Zeilen des Arrays nicht am Stück im Speicher liegen, sondern alle einzeln. Deswegen bekommst du Zugriffsfehler.



  • Zur Not wäre ich auch bereit, das Ganze auf ein eindimensionales Array zu mappen, dieses Array zu senden, und anschließend wieder in ein zweidimensionales Arrays zu verwandeln... Wäre aber eher ne Notlösung.



  • SeppJ schrieb:

    Du hast einen vector von vectoren nehme ich an? Denk daran, dass da die Zeilen des Arrays nicht am Stück im Speicher liegen, sondern alle einzeln. Deswegen bekommst du Zugriffsfehler.

    Ja hab ich. Hmm, das is blöd, ich bin nicht zwingend auf die Vectorklasse angewiesen. Würde es mit normalen Arrays funktionieren? (Kanns gerad nicht testen, da ich nich zu Hause bin)


  • Mod

    kaesekuchen86 schrieb:

    Würde es mit normalen Arrays funktionieren? (Kanns gerad nicht testen, da ich nich zu Hause bin)

    Jein. Es geht hier nicht um vector<->Array. Es geht mit beiden. Entscheidend ist, wie die Daten organisiert sind. Du hast derzeit etwas wie vector<vector<data> > . Dies entspricht in Array und Pointerschreibweise einem data** . Da ist alles dynamisch (Anzahl der Zeilen und jeweilige Länge der Zeilen), daher geht beides nicht wenn du es behandelst als würde es hintereinander im Speicher liegen.
    Was du brauchst ist eine quasi-eindimensionale Struktur. Wenn die Länge der Zeilen immer gleich ist, ist das kein Problem und du kannst dafür entweder ein Array von Arrays nehmen oder komfortabler einen vector von einer Arrayklasse*. Konkret könnte man dies so machen:

    template<class T, size_t size> struct Array
    {
     T Daten[size];
    };
    
    // ...
    
    vector<Array<data, N> > array;
    

    Wenn deine Zeilen nicht alle gleich lang sind, hast du ein Problem. Dann führt nichts daran vorbei die Zeilen jeweils einzeln zu behandeln.

    *: vector von Arrays geht nicht, weil die Vectorklasse eine Kopiersemantik für die Elemente voraussetzt.



  • Danke sehr!

    Damit ich dich richtig verstehe:
    Bei mir ist die Länge der Zeilen und auch die Länge der Spalten immer gleich. Es reicht also ein ganz normales zweidimensionales Array

    complex<double> array[n][n]
    

    aus?


  • Mod

    Wäre eine Möglichkeit. Sofern n groß ist, solltest du die Daten auf den Heap packen.



  • Super, habs getestet und funktioniert!
    Allerdings würde ich das Array tatsächlich gerne auf dem Heap anlegen, da es sehr groß ist.
    Ich kenn mich mit dem new Befehl nicht so aus, sind die Daten dann hintereinander im Speicher?
    Wie initialisiert man das Array genau? Bei eindimensionalen Arrays ist es ja einfach.
    Kannst du vielleicht ein kleines Mininmalbeispiel posten? Am besten ohne templates usw...(davon hab ich nämlich noch gar keine Ahnung^^)?

    Vielen Dank jedenfalls für deine Hilfe, du hast mir schon extrem weiter geholfen! (Generell klasse Forum hier)



  • Mit new kannst du keine mehrdimensionalen (im eigentlichen Sinne) Felder erzeugen. Aber die sind ja auch kein Hexenwerk... Nimm ein eindimensionales Feld und lege darin die Zeilen oder Spalten hintereinander, je nach Bedarf. Wenn du dann wirklich ein zweidimensionales brauchst (die Indizes auf 1D umzurechnen finde ich irgendwie angemeher), kannst du zusätzlich noch ein feld von Zeigern erstellen, die dann jeweils auf die entsprechenden Zeilen zeigen... Funktioniert natürlich nur gut, wenn man feste Zeilen- order Spaltenanzahl hat.



  • kaesekuchen86 schrieb:

    Danke sehr!

    Damit ich dich richtig verstehe:
    Bei mir ist die Länge der Zeilen und auch die Länge der Spalten immer gleich. Es reicht also ein ganz normales zweidimensionales Array

    complex<double> array[n][n]
    

    aus?

    Ich würde mir dafür einfach eine Wrapper Klasse schreiben, die das ganze sauber verwaltet.

    #include <complex>
    #include <stdexcept>
    #include <cstdlib>
    
    class Matrix {
        size_t                n_,m_;
        std::complex<double>* field_;
    public:
        Matrix (size_t n, size_t m) : n_(n), m_(m) {
            if ((n_ == 0) || (m_ == 0)) {
                 throw std::logical_error("one dimension is zero");
            }
            field_ = new std::complex<double>[n*m];
        }
        ~Matrix () {
             delete[] field_;
        }
    
        std::complex<double>& operator()(size_t i,size_t j) {
            return field_[n_*i+j];
        }
    };
    

    Den Rest (CC & Co) kannst Du schnell selbst schreiben.


Anmelden zum Antworten