Mehrkernanwendung möglich/passender Einstieg



  • Guten Abend,

    ich habe eine sehr große for-Schleife mit einer aufwendigen Berechnung.
    Mein Programm nutzt bis jetzt nur einen von vier Kernen (Berechnungsdauer 2 Tage).

    Jetzt möchte ich die for-Schleife in 4 Teile aufteilen und die 4 Teile in den 4 Kernen berechnen lassen.
    Die Ergebnisse aus jedem Teil sollen in ein gleiches Array gespeichert werden und wenn alle 4 Teile fertig berechnet sind, soll das Array weiter verarbeitet werden (dann wieder nur mit einem Kern).

    Ist das so möglich?
    Ich habe noch nie mit Mehrkernanwendungen hantiert. Hat jemand vielleicht ein Beispielprogramm oder einen guten Link, damit ich mich in dieses Kapitel einlesen kann?

    Vielen dank für eure Hilfe



  • cori-schnori schrieb:

    Jetzt möchte ich die for-Schleife in 4 Teile aufteilen und die 4 Teile in den 4 Kernen berechnen lassen.

    Das hört sich für mich nicht nach einer guten Parallelisierung aus. Kann aber sein dass ich mich irre - kenne ja die Berechnung nicht.

    Kannst du etwas konkreter sagen was die Berechnung macht und eventuell auch mal den Code der Berechnung posten damit wir sehen was man da parallelisieren kann?



  • cori-schnori schrieb:

    Ist das so möglich?
    Ich habe noch nie mit Mehrkernanwendungen hantiert. Hat jemand vielleicht ein Beispielprogramm oder einen guten Link, damit ich mich in dieses Kapitel einlesen kann?

    Nein, in das Thema Parallelität liest man sich nicht mal eben so schnell rein, dass man ein Top-Programm zaubert.

    Eine Parallelisierung hinzubekommen ist gar nicht mal so schwer, auch wirst du ein Performanceplus wahrscheinlich feststellen (aber mit einem niedrigen Effizienzgrad)). Aber da gibt es so viel falsch zu machen, das ist nicht mal eben so erklärt, das kommt auf die Berechnung und den Algorithmus (und auch teilweise auf die Daten die berechnet werden sollen) an.
    Und wenn du grobe Schnitzer machst, dann wirst du sogar länger brauchen.

    Sag uns was du machst, wie du es machst und dann sehen wir weiter.



  • Hast Du es schon mal mit threads versucht?



  • ...wenn du ein paar offene Fragen klärst

    OS: Win32, Linux, ...?
    Sprache: C++03, C++11?
    Framework: ?

    Können die 4 Teile völlig unabhängig voneinander rechnen?
    es gibt also keine schreibe/lese-Abhängigkeit außer
    zum Ergebnis-Array? Mehr Details bitte

    dann wäre die Antwort ja! ist ganz leicht



  • Ich denke, ihr übertreibt alle ziemlich. Gerade reine Berechnungen sind relativ einfach zu parallelisieren und erzielen auch proportionale Performance.
    Solange du unter C++11 kompilierst, sollte auch dein OS egal sein.

    Wenn du einfach nur eine Schleife parallelisieren willst, reicht dafuer eine Zeile OpenMP. "#pragma omp parallel for", fertig. Ansonsten waere es gut, wenn du Code zeigen koenntest, damit wir dir besser helfen koennen.



  • Ja, genau das ist doch unser Reden. Aber es kommt ja kein Code und keien Antwort...



  • Teilweise braucht man doch selber gar nichts machen? Es gibt doch von Intel Precompiler-Makros die man nur um die Schleife setzen braucht, und der Rest geht von alleine.
    Selbst im MS-Compiler ist das mittlerweile verfügbar.

    http://de.wikipedia.org/wiki/OpenMP#Beispiel-Code



  • Klar, aber trotzdem muss/sollte man sich ein paar Gedanken darum machen.
    Denn alles ist nicht so ohne Weiteres parallelisierbar.

    Item * top = // ... gefüllte verkette Liste
    
    Item * current = top;
    while ( current != nullptr )
    {
    	current->someFunc(); // teuer
    	current = current->next;
    }
    

    Nicht so mal eben mit #pragma omp parallel machbar...
    (Und ja, den Trick bzw. die Lösung kenne ich auch, schwer ists ja nicht)



  • (Und ja, den Trick bzw. die Lösung kenne ich auch, schwer ists ja nicht)

    Die da wäre? Rein interessehalber.



  • Item * top = // ... gefüllte verkette Liste
    // ...
    Item * items[size]; 
    Item * current = top;
    uint i = 0;
    while ( current != nullptr )
    {
    	items[i++] = current;
        current = current->next;
    }
    // ..
    #pragma omp parallel for
    for(int i = 0; i < size; i++)
    	items[i]->someFunc();
    

    Lohnt sich natürlich nur wenn die aufzurufende Funktion teuer genug ist...



  • Hab mich jetzt mal ein bisschen mit openMP gepsielt.
    Also prinzipell bedeutet das aufteilen auf mehrer Threads, bzw. die verwendung von z.B.:

    #pragma omp parallel for
    

    nicht unbedingt mehr "Geschwindigkeit" oder?

    #pragma omp for 
    for (int i = 0; i < 100000; i++){
    
       std::cout << "Here comes i : " << i << "\n";
    }
    

    ist jetzt nicht unbedingt schneller wie ohne #pragma omp for ??



  • du solltest dir überlegen wie du es machen möchtest, also ob ein datenaustausch zwischen den threads stattfinden soll...

    ansonsten ein paar c++ threads: 😃 (kommt halt drauf an was und wie du ein problem lösen willst welcher für dich passen könnte)

    std::thread -> c++11
    async -> asynchrone threads
    pthreads ...

    ansonsten gab es auch noch threads denen du die kerne deiner Hardware festzuweisen kannst... also z.b. kern 1 macht das ... etc... da weiß ich jetzt aber gerade nicht wie die funktion heißt... sry hab auch keine zeit für ne kurze recherche... 🙄

    ich kann dir nur eines von diesen empfehlen ...
    man findet viel information unter google dazu und syntaktisch komplex ist es auch nicht wirklich...
    hoffe ich konnte dir wenigstens ein bisschen helfen... 😉



  • du kannst dich auch mit den themen:

    synchrone threads, asynchrone threads
    was ist das?
    unterschiede?

    was ist paralellisierung und wozu kan man threads benutzen?

    mir hat das damals als ich angefangen habe sehr geholfen... 🙂
    vorallem welche thread- möglichkeit man nutzt und welche besser nicht...



  • unsure110 schrieb:

    Hab mich jetzt mal ein bisschen mit openMP gepsielt.
    Also prinzipell bedeutet das aufteilen auf mehrer Threads, bzw. die verwendung von z.B.:

    #pragma omp parallel for
    

    nicht unbedingt mehr "Geschwindigkeit" oder?

    #pragma omp for 
    for (int i = 0; i < 100000; i++){
    
       std::cout << "Here comes i : " << i << "\n";
    }
    

    ist jetzt nicht unbedingt schneller wie ohne #pragma omp for ??

    Wie gesagt, das kommt drauf an.
    Dein Beispiel hier ist kein gutes Beispiel, da du zum ersten Seiteneffekte hast (die Bildschirmausgabe) und zum zweiten die Consolenausgabe eben ganz tief intern doch mit sowas umgehen kann. D.h. du hast doch eine Synchronisation, was ein dickes Performanceminus aufwirft. Und zum dritten, wähle Zahlen die hinreichend groß genug sind. Bis 100000 zählen kann auch mein Taschenrechner in 0,nix. Und zum vierten reicht #pragma omp for nicht aus, das muss entweder in einem parallel-Block liegen oder #pragma omp parallel for heissen.

    Probier einfach mal dieses (Mehr oder weniger sinnlose) Programm. Den Parameter p kannst du nach belieben verändern:

    #include <iostream>
    #include <chrono>
    #include <vector>
    
    #include <omp.h>
    
    int func(int n)
    {
    	int r = 0;
    	for (int i = 0; i < n; i++)
    	{
    		if (i % 2 == 0)
    			r += i;
    		else
    			r -= i;
    	}
    
    	return r;
    }
    
    int main()
    {
    	const int p = 8;
    
    	int N = 100000;
    	std::vector<int> vec(N);
    
    	if ( !(p < 1 ))
    		omp_set_num_threads(p);
    
    	auto t1 = std::chrono::high_resolution_clock::now();
    	{
    #pragma omp parallel for
    		for (int i = 0; i < vec.size(); i++)
    		{
    			vec[i] = func(i);
    		}
    
    		int result = 0;
    #pragma omp parallel for reduction(+:result)
    		for (int i = 0; i < vec.size(); i++)
    			result += vec[i];
    
    		std::cout << "R: " << result << std::endl;
    	}
    	auto t2 = std::chrono::high_resolution_clock::now();
    
    	auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1).count();
    
    	std::cout << "Duration: " << duration << std::endl;
    
    	return 0;
    }
    


  • @Kellerautomat
    Ich glaube du hattest noch keinen Kontakt mit diversen Stolpersteinen und stellst dir die Sache daher etwas zu einfach vor.
    Oder hast sie einfach nicht bemerkt - gerade beim Thema Threads kann man viele Fehler jahrelang in einem Programm haben ohne draufzukommen.

    Klar geht es manchmal einfach.
    Wenn der Auto-Parallelizer es packt, fein.

    Ansonsten... einfach mal #pragma omp parallel for vor den Loop packen und gut. Oder eigentlich weniger gut. Es sei denn man findet Schmerzen und schwer bis gar nicht reproduzierbare Fehler gut.
    Man muss schon verstehen was man mit OpenMP tut. Und das lernt man halt nicht im Schlaf.



  • unsure110 schrieb:

    H

    #pragma omp for 
    for (int i = 0; i < 100000; i++){
    
       std::cout << "Here comes i : " << i << "\n";
    }
    

    ist jetzt nicht unbedingt schneller wie ohne #pragma omp for ??

    Da ist wahrscheinlich die Konsolen-Ausgabe der Flaschenhals. Mach mal ein paar Berechnungen ohne Ausgabe... und gebe die verbrauchte Zeit aus.



  • Wenn es wirklich der Fall sein sollte, dass man nur eine große Schleife parallelisieren möchte sollte OpenMP das Mittel der Wahl sein, da am nächsten am Problem dran. Das "einzige" was man dazu noch wissen muss ist: Welche Variablen müssen Thread-lokal und welche gemeinsam sein und wie bekomme ich die Daten am Schluss wieder wie gewünscht zusammen. In einfachen fällen weiß man das nach einem kurzen Tutorial.



  • Wenn es wirklich der Fall sein sollte, dass man nur eine große Schleife parallelisieren möchte sollte OpenMP das Mittel der Wahl sein, da am nächsten am Problem dran. Das "einzige" was man dazu noch wissen muss ist: Welche Variablen müssen Thread-lokal und welche gemeinsam sein und wie bekomme ich die Daten am Schluss wieder wie gewünscht zusammen. In einfachen fällen weiß man das nach einem kurzen Tutorial.

    Und

    Dein Beispiel hier ist kein gutes Beispiel, da du zum ersten Seiteneffekte hast (die Bildschirmausgabe) und zum zweiten die Consolenausgabe eben ganz tief intern doch mit sowas umgehen kann. D.h. du hast doch eine Synchronisation, was ein dickes Performanceminus aufwirft. Und zum dritten, wähle Zahlen die hinreichend groß genug sind. Bis 100000 zählen kann auch mein Taschenrechner in 0,nix. Und zum vierten reicht #pragma omp for nicht aus, das muss entweder in einem parallel-Block liegen oder #pragma omp parallel for heissen.

    Ehrlich gesagt ich hab mich nicht eingelesen, sondern nur den wikipedia Artikel überflogen und der größte Kampf war meiner IDE zu erklären wie sie das ganze Compliieren muss 🙂

    Ist aber ziemlich interssant, werd mich da mal einlesen, obwohl ich das wohl nie brauchen werde 🙂

    Hat jmd. ein empfehlenswertes Tutorial zu der Thematik?



  • unsure110 schrieb:

    Hat jmd. ein empfehlenswertes Tutorial zu der Thematik?

    Ja, besuch die entsprechendeN VorlesungEN an der nächsten Uni 😃

    Hier im Forum gibts nen kleinen Artikel über OpenMP.

    Ich hab irgendwie das Gefühl meine Beiträge werden ignoriert 😃

    Artchi schrieb:

    unsure110 schrieb:

    H

    #pragma omp for 
    for (int i = 0; i < 100000; i++){
    
       std::cout << "Here comes i : " << i << "\n";
    }
    

    ist jetzt nicht unbedingt schneller wie ohne #pragma omp for ??

    Da ist wahrscheinlich die Konsolen-Ausgabe der Flaschenhals. Mach mal ein paar Berechnungen ohne Ausgabe... und gebe die verbrauchte Zeit aus.

    😃


Log in to reply