C++ parallele Rechnungen
-
Hi,
soweit ich gehört und gelesen habe, ist es eine Möglichkeit seine Rechnungen zu beschleunigen, wenn man sie parallel auf mehreren Kernen rechnen lässt.
Jetzt sieht es so aus, dass ich gerne Monte Carlo Rechnungen durchführen würde, d.h. um eine möglichst gute Aussagekraft zu erzielen muss meine Rechnung sehr oft wiederholt werden, um anschließend zu mitteln. Wenn ich das richtig verstanden habe, dann habe ich das Glück, dass diese ganzen Rechnungen unabhängig voneinander sind. Mit anderen Worten ich muss mich um dieses Message Passing Interface (MPI) gar nicht kümmern, weil zwischen den Rechnungen keine Informationen ausgetauscht werden müssen. Eine Rechnung muss nicht auf die andere warten.
Jetzt hat mein PC also vier Kerne und ich stelle mir die naive Frage: Werden die nicht alle verwendet, um meine Rechnung auszuführen? Also intern muss der PC doch die Aufgabe auf die vier Kerne verteilen, sonst hätte es ja überhaupt keinen Vorteil mehrere Kerne zu besitzen.
Weiterhin heißt es jetzt, dass Linux für jeden Start eines Jobs einen eigenen Kern benötigt, um das zu prüfen wurde mir z.B. top empfohlen.
Also einfach mal ausprobieren: Zwei Konsolen öffnen, darin jeweils das Programm starten (./mainWater) und dann in einer dritten Konsole top eintippen.
:~$ top top - 08:39:07 up 1:23, 4 users, load average: 0.57, 0.13, 0.04 Tasks: 159 total, 4 running, 155 sleeping, 0 stopped, 0 zombie Cpu(s): 50.3%us, 0.0%sy, 0.0%ni, 49.7%id, 0.0%wa, 0.1%hi, 0.0%si, 0.0%st Mem: 8109652k total, 1074264k used, 7035388k free, 72800k buffers Swap: 2579448k total, 0k used, 2579448k free, 514304k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4505 klaus 20 0 18372 1860 1420 R 100 0.0 0:19.08mainWater 4506 klaus 20 0 18372 1856 1420 R 100 0.0 0:17.04mainWater 1939 root 20 0 245m 94m 8732 S 1 1.2 1:29.70Xorg 2811 klaus 20 0 223m 15m 10m S 1 0.2 0:01.58 gnometerminal 1 root 20 0 8356 812 680 S 0 0.0 0:00.89init 2 root 20 0 0 0 0 S 0 0.0 0:00.00 kthreadd
Aha, also die Jobs mit den Nummern 4505 und 4506 besitzen beide 100% in der CPU.
So, wenn ich also vier Kerne habe, dann sollte sich das verändern, wenn ich fünf Konsolen öffne und das insgesamt fünf Mal starte:
:~$ top top - 08:52:36 up 1:36, 7 users, load average: 2.21, 0.57, 0.19 Tasks: 165 total, 7 running, 158 sleeping, 0 stopped, 0 zombie Cpu(s):100.0%us, 0.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 8109652k total, 1094408k used, 7015244k free, 74544k buffers Swap: 2579448k total, 0k used, 2579448k free, 515524k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4623 klaus 20 0 18372 1852 1416 R 92 0.0 0:08.44mainWater 4602 klaus 20 0 18372 1856 1420 R 85 0.0 0:27.77mainWater 4551 klaus 20 0 18372 1856 1420 R 83 0.0 0:46.26mainWater 4550 klaus 20 0 18372 1856 1420 R 69 0.0 0:46.53mainWater 4595 klaus 20 0 18372 1856 1420 R 68 0.0 0:33.36mainWater 2434 klaus 20 0 776m 142m 29m S 1 1.8 3:14.02firefox-bin 1939 root 20 0 245m 95m 8736 S 1 1.2 1:36.32Xorg 2811 klaus 20 0 224m 15m 10m S 0 0.2 0:01.95gnome-terminal 4549 klaus 20 0 19072 1416 1020 R 0 0.0 0:00.05top 1 root 20 0 8356 812 680 S 0 0.0 0:00.90init 2 root 20 0 0 0 0 S 0 0.0 0:00.00kthreadd
Aha, scheint also auch zu funktionieren, die fünf Jobs liegen deutlich unter 100%.
Frage 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?
Wobei dies natürlich direkt zum nächsten Problem führt. Zufallszahlen sind leider keine echte Zufallszahlen. D.h. falls ich das Programm vier mal starte, würde vier mal das Gleiche herauskommen, da ich den seed für den random number generator ja nicht verändere.
D.h. bei dem Aufruf von ./mainWater müsste ich die Möglichkeit haben eine Zahl für den Seed zu übergeben. Ich müsste also die ausführbare Datei mainWater genauso anlegen wie z.B. eine Funktion unter C++ mainWater(int seed). Geht das? Das wäre schon ziemlich krass.Weiterhin mache ich es bisher so dass am Ende des Programms die Daten in eine Datei geschrieben werden, um sie graphisch aufzutragen. Das heißt, wenn ich das Programm vier Mal ausführe, dann befürchte ich, dass die Datei nicht vier Mal erzeugt wird, sondern immer wenn ein Programm durch ist die vorherige Datei überschreibt.
Zum Einen müsste ich also neben der Zahl für den Seed auch die zahl für den Job an mainWater übergeben: mainWater(int seed, int run), um den integer run dann an den Dateinamen weiterzugeben.
Ganz elegant wäre es natürlich wenn man insgesamt ein Skript schreiben könnte, welches die vier Jobs startet, deren Ergebnisse sofort mittelt und am Ende wieder nur eine Datei herauskommt.
Das fände ich alles schon ziemlich cool und geht auch bestimmt!
Aber ich habe die Befürchtung, dass diese einfachen Wünsche doch wieder einiges an Einarbeit erfordern, obwohl ich die Hoffnung hege, dass es sich in Grenzen hält, weil ich ja kein MPI betreiben muss, weil die Prozesse unabhängig voneinander laufen.
Viele Grüße und Danke im Voraus,
Klaus.
-
Message Passing Interface (MPI)
Du solltest dich erstmal mehr informieren. MPI ist fuer Cluster mit non-shared-memory. Ein 4, 6 oder 48-Kern-Prozessor ist keine shared-memory-Architektur.
Also intern muss der PC doch die Aufgabe auf die vier Kerne verteilen, sonst hätte es ja überhaupt keinen Vorteil mehrere Kerne zu besitzen.
Ein Computer tut nichts, solange du ihm nicht die Anweisung dafuer gibst.
Weiterhin heißt es jetzt, dass Linux für jeden Start eines Jobs einen eigenen Kern benötigt
Nein, Multithreading ist auch mit einem Kern moeglich.
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 müsste also die ausführbare Datei mainWater genauso anlegen wie z.B. eine Funktion unter C++ mainWater(int seed). Geht das? Das wäre schon ziemlich krass.
Ja, nennt sich Kommandozeilenparameter.
Das heißt, wenn ich das Programm vier Mal ausführe, dann befürchte ich, dass die Datei nicht vier Mal erzeugt wird, sondern immer wenn ein Programm durch ist die vorherige Datei überschreibt.
Auch das kann durch die Angabe des Dateinamens ueber Kommandozeilenparameter geregelt werden.
Ganz elegant wäre es natürlich wenn man insgesamt ein Skript schreiben könnte, welches die vier Jobs startet, deren Ergebnisse sofort mittelt und am Ende wieder nur eine Datei herauskommt.
Ja, geht. Natuerlich ist Einarbeitung erforderlich.
Komme wieder, wenn du konkrete Fragen hast!
-
Hi,
knivil schrieb:
MPI ist fuer Cluster mit non-shared-memory. Ein 4, 6 oder 48-Kern-Prozessor ist keine shared-memory-Architektur.
Nun, meine Antwort ist jetzt ein wenig gewagt, aber dass ich das für Rechnungen auf Clustern benötige bestärkt mich nur darin, dass ich mich damit erstmal nicht beschäftigen muss, wenn ich das ganze auf meinem Heim PC erreichen möchte.
Falls meine obigen Überlegungen für meinen Heim PC quatsch sind, dann können wir den Thread auch einfach ganz schnell schließen.
Gruß,
Klaus.
-
Nun, meine Antwort ist jetzt ein wenig gewagt, aber dass ich das für Rechnungen auf Clustern benötige bestärkt mich nur darin, dass ich mich damit erstmal nicht beschäftigen muss, wenn ich das ganze auf meinem Heim PC erreichen möchte.
Du hast wohl den Satz davor ignoriert. Es sollte zum Ausdruck bringen, dass du zu wenig Ahnung von der Materie hast. Da dir Kommandozeilenparameter unbekannt sind, solltest du dich erstmal weiter mit Grundlagen beschaeftigen.
-
Hi,
knivil schrieb:
Nun, meine Antwort ist jetzt ein wenig gewagt, aber dass ich das für Rechnungen auf Clustern benötige bestärkt mich nur darin, dass ich mich damit erstmal nicht beschäftigen muss, wenn ich das ganze auf meinem Heim PC erreichen möchte.
Du hast wohl den Satz davor ignoriert. Es sollte zum Ausdruck bringen, dass du zu wenig Ahnung von der Materie hast.
Also werde ich mal nach Kommandozeilenparameter (und damit shell scripting?) und Multithreading suchen und mich damit beschäftigen.
Vielen Dank!
knivil schrieb:
Komme wieder, wenn du konkrete Fragen hast!
Mach ich!
Gruß,
Klaus.
-
Klaus82 schrieb:
Also werde ich mal nach Kommandozeilenparameter (und damit shell scripting?) und Multithreading suchen und mich damit beschäftigen.
Nein, du brauchst keine Shellscripe um Kommandozeilenparameter zu verwenden. Die kannst du in jedem primitiven C++-Programm benutzen. Genauso wie unter C und wohl jeder anderen Programmiersprache da draußen auch.
-
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.
-
int main(int argc, char** argv)
argc: Anzahl der Kommandozeilenparameter
argv: Array mit Kommandozeilenparameter als nullterminierte Char-ArraysBeispiel: ./halloWelt 89 test
argc: 3
argv[0] = "halloWelt"
argv[1] = "89"
argv[2] = "test"
-
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.
-
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.
-
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.