C++ parallele Rechnungen



  • Hi,

    dafuqDidIread schrieb:

    knivil schrieb:

    Also um es mal ganz konkret zu formulieren: Sagen wir mein Programm soll für eine Mittelwertbildung 1000 mal eine for-Schleife durchlaufen. Das würde schneller gehen, wenn die for-Schleife nur 250 Mal durchlaufen wird, aber ich das Programm vier Mal starte?

    Ja.

    Ich glaube dem OP ist nicht klar, dass die Ergebnisse eine parallelisierten Berechnung am Ende synchron zusammengeführt werden müssen.

    Wie sich gezeigt hat, habe ich nahezu Null Ahnung, aber ich behaupte, dass ich zumindest so viel verstanden habe, dass meine Rechnungen im Zuge der Monte Carlo Methode voneinander unabhängig sind.

    D.h. ob ich eine Rechnung mit einer for-Schleife von 1000 Durchläufen habe, oder vier Rechnungen mit je 250 Durchläufen pro Schleife, macht für mich keine Unterschied.

    Im zweiten Fall muss ich nachträglich manuell noch die Ergebnisse mitteln, klar - aber ich mache ja nichts 'falsch'.

    Die Rechnungen müssen ja keine Informationen untereinander austauschen.

    Kann sein, dass ich deinen Einwand nicht verstanden habe, weil ich zu wenig Ahnung habe, aber was genau sollte den synchron zusammengeführt werden müssen?

    Gruß,
    Klaus.



  • aber ich mache ja nichts 'falsch'

    Wenn du den Zufallsgenerator mit dem gleichen Seed initialisierst, bringt es dir rein gar nichts. Zeige doch mal die 3 Codezeilen fuer die Initialisierung des Zufallsgenerators!



  • knivil schrieb:

    aber ich mache ja nichts 'falsch'

    Wenn du den Zufallsgenerator mit dem gleichen Seed initialisierst, bringt es dir rein gar nichts. Zeige doch mal die 3 Codezeilen fuer die Initialisierung des Zufallsgenerators!

    Äh ja,

    das hatte ich doch weiter oben auch geschrieben als Problematik. Aus dem Grund der Hinweis, dass ich mittels Kommandozeilenparameter einen Seed an den rng übergeben kann, d.h. ich starte das Programm vier mal, aber mit einem anderen seed.

    Gruß,
    Klaus.



  • Jetzt macht es dem Klaus doch nicht so schwer 😉

    Du kannst, wie auch schon erwähnt, ein Programm mit Parametern starten.
    Sagen wir dein Parameter ist jetzt dein Seed, dann kannst du im Programm selbst wieder 4 neue Seeds ( rand(), srand() oder andere / bessere ) erstellen.

    Du kannst aber natürlich auch direkt 4 Seeds übergeben!

    "./mainWater 1 2 3 4"

    Das parallele rechnen kannst du mit fork() oder mit threads erledigen.

    und mit dem synchronen zusammenführen dürfte sowas wie "darauf achten dass wirklich alles fertig ist, parallele zugriffe auf gleiche Variablen" usw gemeint sein.



  • knivil schrieb:

    MPI ist fuer Cluster mit non-shared-memory. Ein 4, 6 oder 48-Kern-Prozessor ist keine shared-memory-Architektur.

    Man kann MPI auch auf einem Rechner laufen lassen und meiner Erfahrung nach ist das sogar effektiver als pThreads, wenn viel Kommunikation nötig wird.

    Viele pthread-Funktionen scheinen auch nicht allzu performant zu sein (barrier zB).



  • Hallo ihr Lieben,

    ich möchte ja nach wie vor auf mehreren Kernen meines PCs arbeiten.

    Ich habe angefangen ein ganz einfaches Programm zu schreiben, dass als Eingabe in int main() die Zahl bekommt, wie oft es Hello World ausgeben soll.

    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    
    int main(int argc, char** argv)
    {
    
    	int n = atoi(argv[1]);
    
    	for(int i = 0; i < n; ++i)
    	{
    		std::cout << "Hello World" << std::endl;
    	}
    
    	return 0;
    }
    

    Jetzt habe ich wiederum ein shell Skript geschrieben, dass das Programm drei Mal aufruft:

    #!/bin/bash
    
    for i in 'seq 1 3'
    do
    ./mb $1
    done
    

    Also hier jetzt die naive Frage: Wenn mein shell Skript nun das Programm drei Mal startet - tut es dann jeweils auf einem neuen Kern? Oder muss ich diesen 'Wunsch' nochmal als eigenen Befehl mitgeben?

    Gruß,
    Klaus.


  • Mod

    So wie du es derzeit startest werden die Programme nacheinander ausgeführt.

    Wenn du es parallel startest, entscheidet der Scheduler wo die Programme ausgeführt werden und du wirst feststellen, dass er das in der Regel sehr gut verteilt (er achtet sogar auf den Unterschied zwischen echten und virtuellen Kernen). Bei einem "Hello World", wird er aber gar nicht dazu kommen, das richtig aufzuteilen, weil viel zu kurze Laufzeit.

    edit: Und bitte lern erst einmal C oder C++. Wie kann man in einem Hello-World noch so viele Fehler haben, aber gleichzeitig 😉 über parallele Programmierung nachdenken?



  • Guten Morgen,

    SeppJ schrieb:

    edit: Und bitte lern erst einmal C oder C++. Wie kann man in einem Hello-World noch so viele Fehler haben, [..]

    Was genau meinst du? Das einzige wobei ich mir unsicher bin ist dieses atoi, es funktioniert zunächst - doch selbst auf zugehörigen C++ Reference Eintrag stehen ja ein paar Bedenken dazu. Akzeptiert.

    Aber was sind denn hier noch so viele Fehler?

    Gruß,
    Klaus.


  • Mod

    Kommt drauf an. Ich bin mir momentan nicht einmal sicher, ob das C oder C++ sein soll. Dies ist auch ein großer Kritikpunkt. Jedenfalls scheinst du das Beispiel aus der Referenz zu atoi blind übernommen zu haben, ohne genau zu verstehen, warum welche Zeile wofür da ist. Wie schon gesagt, macht selbst dein Bashscript nicht das was du möchtest.

    Aber um mal Klartext zu sprechen: Wie viel Erfahrung* in C++ hast du? Tage? Ein paar Wochen? Das ist zu wenig um auch nur anzufangen, über Parallelisierung nachzudenken. Du brauchst dich nicht irgendwie vor mir zu rechtfertigen. Ich erkenne deine Kenntnisse und gebe meinen Rat. Den kannst du annehmen oder ignorieren. Ich bin bloß irgendein Typ im Internet. Vielleicht sitze ich gerade neben dir im Büro, vielleicht am anderen Ende der Welt. Wir werden es nie erfahren.

    *: du hast offensichtlich im März schon programmiert, aber wie viele Tage hast du seitdem tatsächlich mit programmieren verbracht?



  • SeppJ schrieb:

    Aber um mal Klartext zu sprechen: Wie viel Erfahrung* in C++ hast du? Tage? Ein paar Wochen?

    Das ist jetzt für mich wieder eine schwierige Frage, da ich den Eindruck habe, dass das Niveau was Programmieren angeht hier allgemein sehr hoch ist [1] 😉 .

    Also ich hatte in der Schule nie Informatik und hatte es auch während meines Studiums nicht als Nebenfach o.ä.
    Irgendwann nahm ich dann einen HiWi Job an, um ein Gerät mit Labview anzusteuern was dazu geführt hatte, dass mein Einstieg in Programmieren über Labview war, wenn das hier was zählt. 😉

    Ansonsten bin ich jetzt seit ca. 2 Jahren kontinuierlich damit beschäftigt zu programmieren. wobei das eben in zwei Richtungen läuft. Zum Einen in die Richtung, dass das Programm tut was ich will und zum Anderen in die Richtung noch genauer verstehen zu wollen warum es das tut, immerhin habe ich das alles nie von Grund auf gelernt.

    Wobei ich glaube das letzte viertel Jahr mit am Meisten gelernt zu haben.

    Gruß,
    Klaus.

    [1] Was ich ja an sich begrüße. 🙂



  • SeppJ schrieb:

    Aber um mal Klartext zu sprechen: Wie viel Erfahrung* in C++ hast du? Tage? Ein paar Wochen?

    Ach das war eine rhetorische Frage. Tut mir leid, hatte ich nicht direkt gemerkt ...

    Tja, dann werde ich mich wohl einen anderen Weg suchen müssen.

    Gruß,
    Klaus.



  • Ich hab nicht den ganzen Thread gelesen, aber man kann beim Programmierne nie auslernen. Fakt ist, aber dass man gewisse Sachen einfach im Schlaf können und auch verstehen muss.

    Gerade bei C und C++ ist es so, dass auch Code kompiliert der einfach falsch ist. Das ist nicht nur frustrierend, sondern auch gefährlich, weil das Programm dann komische Dinge macht.

    Und bei der Parallelisierung betritt man praktisch die Hölle des ganzen Elends. Spätestens hier muss man wissen, was man da tut. Sonst hat man einfach keine Chance. Grundlagen sind da absolut notwendig. Auch zum Thema Parallelisierung gibt es dann wieder neue Grundlagen über Synchronisationen, usw.

    Das klingt am Anfang alles so simpel und toll, aber das Problem steckt im Detail.



  • hfghfghfghfg schrieb:

    Und bei der Parallelisierung betritt man praktisch die Hölle des ganzen Elends. Spätestens hier muss man wissen, was man da tut. Sonst hat man einfach keine Chance. Grundlagen sind da absolut notwendig. Auch zum Thema Parallelisierung gibt es dann wieder neue Grundlagen über Synchronisationen, usw.

    Vielleicht reden wir da wieder aneinander vorbei. Soweit ich das mit meinen bescheidenen Kenntnissen überblicke will ich nicht parallelisieren im Sinne von MPI.

    Ich arbeite mit der MC Methode, d.h. ich benötige (sagen wir) 1000 Durchläufe meines Programms, um anschließend einen sinnvollen Mittelwert zu bilden. Und jetzt will ich einfach sagen können:
    Anstatt 1000 Durchläufe auf einem Kern durchzuführen, kann ich doch auch 250 Durchläufe auf vier Kernen laufen lassen und anschließend mitteln.

    Dabei müssen die Kerne ja keine Informationen austauschen.

    Soweit ich das verstanden habe kann ich bei Linux also auch vier mal eine Konsole öffnen und darin das Programm starten und dann mittels des Befehls top sehen, dass alle vier Kerne ausgelastet sind.

    Gruß,

    Klaus.



  • Dann mach dein C-Programm komplett Singlethreaded und simpel und sammel die Daten erst nachher aus einem Shellscript heraus zusammen.

    Oder war das sogar deine ursprüngliche Frage und wir haben sie falsch verstanden?



  • parallel_for_each aus der ppl.



  • Klaus82 schrieb:

    hfghfghfghfg schrieb:

    Und bei der Parallelisierung betritt man praktisch die Hölle des ganzen Elends. Spätestens hier muss man wissen, was man da tut. Sonst hat man einfach keine Chance. Grundlagen sind da absolut notwendig. Auch zum Thema Parallelisierung gibt es dann wieder neue Grundlagen über Synchronisationen, usw.

    Vielleicht reden wir da wieder aneinander vorbei. Soweit ich das mit meinen bescheidenen Kenntnissen überblicke will ich nicht parallelisieren im Sinne von MPI.

    Ich arbeite mit der MC Methode, d.h. ich benötige (sagen wir) 1000 Durchläufe meines Programms, um anschließend einen sinnvollen Mittelwert zu bilden. Und jetzt will ich einfach sagen können:
    Anstatt 1000 Durchläufe auf einem Kern durchzuführen, kann ich doch auch 250 Durchläufe auf vier Kernen laufen lassen und anschließend mitteln.

    Dabei müssen die Kerne ja keine Informationen austauschen.

    Soweit ich das verstanden habe kann ich bei Linux also auch vier mal eine Konsole öffnen und darin das Programm starten und dann mittels des Befehls top sehen, dass alle vier Kerne ausgelastet sind.

    Gruß,

    Klaus.

    Gibt 3 Möglichkeiten,

    entweder du schreibst ein Programm dass am Anfang 4 Threads startet(Threads können parallel ausgeführt werden) du brauchst dann tatsächlich keine synchronisation weil die threads sich nicht dafür interessieren müssen was die anderen tun. Dann wartest du mit deinem main thread bis deine 3 worker threads fertig sind und mittelst ihre ergebnisse.

    zweit Möglichkeit wäre Kindprozesse mit fork zu erstellen, funktioniert glaube ich danna ber nicht auf windows.

    dritte Möglichkeit is wohl die einfachste. Weil keine synchronisation Notwendig ist kannst du einfach mit einem shell script 4mal dein programm starten, übergibst ihnen über den Komandozeilenparameter den Seed oder suchst dir ne bessere Methode an einen Seed zu kommen. Jetzt wartest du in deinem shell script bis die vier programme fertig sind und mittelst die ausgabe.

    Dazu google:
    wie du die ausgabe des programms in eine variable bekommst um danach damit weiter zu rechnen
    wie du mehrere programme gleichzeitig starten kannst aus einem shell script heraus
    wie du in C/C++ kommandozeilen parameter einliest.

    Das müsstest du alles finden.

    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    
    int main(int argc, char** argv)
    {
    
        int n = atoi(argv[1]);
    
        for(int i = 0; i < n; ++i)
        {
            std::cout << "Hello World" << std::endl;
        }
    
        return 0;
    }
    

    was machst du wenn kein parameter übergeben wurde? Dann gibt es argv[1] gar nicht. Deshalb auch das abfragen.



  • tobZel schrieb:

    wie du mehrere programme gleichzeitig starten kannst aus einem shell script heraus

    Also das habe ich mal ganz banal wie folgt gelöst:

    #!/bin/bash
    
    for (( i=0 ; i<$1 ; i++ )); do
    	./main_water $((i+1)) &
    done
    exit 0
    

    Durch das & Zeichen werden die Prozesse in den Hintergrund geschoben und so wird nicht gewartet, bis der erste Prozess fertig ist.

    tobZel schrieb:

    wie du in C/C++ kommandozeilen parameter einliest.

    Das hatten wir ja schon und ich habe dazu ein Beispiel gegeben, auch wenn es natürlich nicht abfragt, ob der User verstanden hat, wie die Eingabe zu erfolgen hat. 😃

    tobZel schrieb:

    wie du die ausgabe des programms in eine variable bekommst um danach damit weiter zu rechne

    Ja, das steht natürlich noch aus. Bisher habe ich einfach die Eingabe für den n-ten Durchlauf an die Funktion weitergegeben, welche die Daten in Dateien speichert.

    void saving::saving_data(int & number)
    {
      std::ostringstream outfilename;
      outfilename << "data_file_nr" << number << ".dat";
      std::string outfile(outfilename.str());
      std::ofstream ofile(outfile.c_str());
    
      for(long int i = 0; i < n; ++i)
      {
        double j = i * dE;
        ofile << j << "\t\t" << energy[i] / runs << std::endl;
      }	
    }
    

    Irgendwie habe ich den Eindruck,dass eine der beiden Zeilen unnötig ist:

    std::string outfile(outfilename.str());
      std::ofstream ofile(outfile.c_str());
    

    Gruß,
    Klaus.



  • Also das Thema hat mir nach wie vor keine Ruhe gelassen und ich habe mich auch mit dem Buch von Rainer Grimm 'bewaffnet'.

    Ich habe versucht ein Beispiel zu Threading mit Übergabe von Parametern zu kompilieren. Das findet sich z.B. auf der verlinkten Seite unter Downloads .

    Ich habe die Beispieldatei createThreadWithArguments.cpp gekürzt, da mich zunächst nur Funktionen interessieren und keine Funktionen von Klassen bzw. Lambdafunktionen:

    /*
     * createThreadWithArguments.cpp
     *
     *  Created on: 24.04.2011
     *      Author: Rainer Grimm
     *      Book  : C++0x
     */
    #include <iostream>
    #include <string>
    #include <thread>
    
    void helloFunction(const std::string& s){
      std::cout << s << std::endl;
    }
    
    int main(){
    
      std::cout << std::endl;
    
      // thread executing helloFunction
      std::thread t1(helloFunction,"Hello C++0x from function.");
    
      // ensure that t1, t2 and t3 have finished before main terminates
      t1.join();
    
      std::cout << std::endl;
    
    }
    

    Es kompiliert vollständig nur leider kriege ich dann bei der Ausführung einen Speicherzugriffsfehler. 😞

    Wenn ich gdb anwerfe, dann sagt er

    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000000000 in ?? ()
    (gdb) bt
    #0  0x0000000000000000 in ?? ()
    #1  0x00002aaaab3ec5d7 in std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) () from /usr/lib/libstdc++.so.6
    #2  0x000000000040127c in main ()
    

    Und Valgrind sagt dazu folgendes:

    ==11149== Command: ./createThreadWithArguments
    ==11149== 
    
    ==11149== Jump to the invalid address stated on the next line
    ==11149==    at 0x0: ???
    ==11149==    by 0x400FCF: ??? (in /var/autofs/home/home/huthmacher/Cpp/sandkasten/createThreadWithArguments)
    ==11149==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
    ==11149== 
    ==11149== 
    ==11149== Process terminating with default action of signal 11 (SIGSEGV)
    ==11149==  Bad permissions for mapped region at address 0x0
    ==11149==    at 0x0: ???
    ==11149==    by 0x400FCF: ??? (in /var/autofs/home/home/huthmacher/Cpp/sandkasten/createThreadWithArguments)
    ==11149== 
    ==11149== HEAP SUMMARY:
    ==11149==     in use at exit: 72 bytes in 1 blocks
    ==11149==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
    ==11149== 
    ==11149== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
    ==11149==    at 0x4C24DFA: operator new(unsigned long) (vg_replace_malloc.c:261)
    ==11149==    by 0x4011DA: main (in /var/autofs/home/home/huthmacher/Cpp/sandkasten/createThreadWithArguments)
    ==11149== 
    ==11149== LEAK SUMMARY:
    ==11149==    definitely lost: 0 bytes in 0 blocks
    ==11149==    indirectly lost: 0 bytes in 0 blocks
    ==11149==      possibly lost: 0 bytes in 0 blocks
    ==11149==    still reachable: 72 bytes in 1 blocks
    ==11149==         suppressed: 0 bytes in 0 blocks
    ==11149== 
    ==11149== For counts of detected and suppressed errors, rerun with: -v
    ==11149== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
    make: *** [memcheck] Speicherzugriffsfehler
    

    Was kann denn bei so einem einfachen Programm schon schief gehen? Ist mein gcc Kompiler nicht aktuell genug? Ich verwende gcc version 4.4.5 (Debian 4.4.5-8) .

    Gruß,
    -- Klaus.



  • Mit welchen Flags hast du denn kompiliert? Du brauchst -pthread und eine aktuelle GLIBCXX.



  • kompail schrieb:

    Mit welchen Flags hast du denn kompiliert? Du brauchst -pthread und eine aktuelle GLIBCXX.

    Aha, so einfach kann es also sein. Sagt einem ja auch keiner. 😉

    g++ -Wall -pedantic -std=c++0x -O2 -c createThreadWithArguments.cpp
    g++ -Wall -pedantic -std=c++0x -O2 -pthread -o "createThreadWithArguments" createThreadWithArguments.o
    

    Dann funzt es auch. 🙂

    Gruß,
    -- Klaus.


Anmelden zum Antworten