PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Threading, Multiprocessing in C/C++


Nasenbaer
2007-05-20, 18:20:49
Hi,
welche Libs würdet ihr für platformübergreifenden Threading und Multiprozess Support für C/C++ empfehlen (möglichst für C++ sprich gut nutzbar mit OOP)?
Hab da von OpenMP gehört und pthreads (Unix) aber gibt doch sicher mehr in dem Bereich?!

rotalever
2007-05-20, 19:56:42
Ich hab mich damit noch nicht näher beschäftigt, will/muss/sollte aber auch bald mal etwas mit Multithreading machen.
Boost hört sich da doch nicht schlecht an, oder? Da wird einem eine plattformübergreifende Möglichkeit geschaffen.

del_4901
2007-05-20, 19:57:18
Ich nehm auch Boost ... geht ganz gut!

Nasenbaer
2007-05-20, 21:57:19
Kannte ich noch gar nicht...

Habs erstmal mit pthreads probiert aber klappt natürlich nicht.
die pthread_Create() übergebene funktion wird irgendwie gar nicht ausgeführt. :/

Nasenbaer
2007-05-20, 22:13:34
So toll jetzt gehts. Bäruchte ich nur noch en System, dass auch nbenläufig arbeiten kann, denn so merk ich natürlich rein gar nichts.

Coda
2007-05-20, 22:37:18
Und du übersiehst potentiell Raceconditions ;)

Nasenbaer
2007-05-20, 22:53:19
Und du übersiehst potentiell Raceconditions ;)
Jo aber bei meiner Beispielapp hab ich keine. (Mache Matrizenmult. für die ersten x zeilen und dann für die restlichen - das ist unabhängig von einander)

Stone2001
2007-05-21, 10:30:18
Jo aber bei meiner Beispielapp hab ich keine. (Mache Matrizenmult. für die ersten x zeilen und dann für die restlichen - das ist unabhängig von einander)
Für MatMult würde ich OpenMP nehmen! Einmal
#pragma omp parallel for
um die äußerste for-Schleife und schon ist das Programm parallelisiert. ;)
Schön ist auch, dass OpenMP jetzt vom GCC 4.2 unterstützt wird.

Mit MPI ist das schon ein etwas größerer Aufwand (man muß die Daten verteilen, dann die Berechungen durchführen und dann das Ergebnis wieder zurück schicken).

Im High Performance Computing-Bereich sind OpenMP und MPI inzwischen Standard, dazu kommen noch viele (ziemlich viele) Altlasten mit Fortran (F90 oder HPF). PThreads werden hier kaum verwendet.

Nasenbaer
2007-05-21, 16:30:57
Jo ich weiß, dass OpenMP für numerische Berechnungen wesentlich besser geeignet ist als PThreads aber das sollte auch nur ein Test sein obs so funktioniert wie gedacht.

Eigentlich will ich das lieber für komplett nebenläufige Sachen haben. Z.B. Trennung zwischen GUI und Berechnung etc.

Weiteres Problem: ICC will ich nicht und GCC 4.2 will ich meinem Gentoo jetzt noch nicht antun. :)

AlSvartr
2007-05-21, 20:06:29
Was heißt besser geeignet, letztlich wird der Krempel mit OpenMP ja auf PThreads abgebildet. Aber klar, es ist wirklich wundervoll einfach :]

Nasenbaer
2007-05-21, 20:14:26
Was heißt besser geeignet, letztlich wird der Krempel mit OpenMP ja auf PThreads abgebildet. Aber klar, es ist wirklich wundervoll einfach :]
Wie Stone2001 schon schrieb. Hier hätte 1 Zeile genügt. Wenn ich pthreads direkt nutze muss man wesentlich mehr machen.

Demirug
2007-05-21, 20:25:50
Genau aus dem Grund ist OpenMP auch so populär. Eine optimale Ausnutzung der Rechenleistung erreicht man damit leider nicht immer.

Wenn man nicht gerade im Numbercruncher Geschäft ist braucht ist ein Multithreaded Job Manager in der Regel sowieso die bessere Wahl.

Nasenbaer
2007-05-21, 20:29:40
Genau aus dem Grund ist OpenMP auch so populär. Eine optimale Ausnutzung der Rechenleistung erreicht man damit leider nicht immer.

Wenn man nicht gerade im Numbercruncher Geschäft ist braucht ist ein Multithreaded Job Manager in der Regel sowieso die bessere Wahl.
Was ist das letztere genau? Vielleicht en Link oder en Beispiel damit ich mich darüber mal belesen kann? :)

Demirug
2007-05-21, 21:01:38
Die Links die ich jetzt gerade zur Hand habe führen auf Passwort geschützte Seiten.
Aber such mal nach Thread pooling. Das ist die einfachste Form eines Job Managers. „moderne Programmiersprachen“ haben das meist schon in ihrer Klassenbibliothek.

Silpion
2007-05-31, 18:45:50
Hmm... ich habe gerade ein merkwürdiges Problem mit zwei Threads.

Thread 1 und 2 arbeiten nacheinander Knoten einer verketteten Liste ab. Thread 1 ist dabei immer vor Thread 2. Erst wenn Thread 1 einen Knoten freigibt (locked=false), greift ihn Thread 2 auf, setzt locked sofort wieder auf true und arbeitet damit.

Im folgenden Bild fragt Thread 2 ab, ob der nächste Knoten freigegeben ist. locked des nächsten Knotens steht auf false. Trotzdem verzweigt die Abfrage, als müsste der Thread warten, bevor er anfangen darf zu arbeiten.

http://images.seelge.de/if_false.png

Irgendwelche Ideen, woran dies liegen könnte?

del_4901
2007-05-31, 21:05:20
Hmm... ich habe gerade ein merkwürdiges Problem mit zwei Threads.

Thread 1 und 2 arbeiten nacheinander Knoten einer verketteten Liste ab. Thread 1 ist dabei immer vor Thread 2. Erst wenn Thread 1 einen Knoten freigibt (locked=false), greift ihn Thread 2 auf, setzt locked sofort wieder auf true und arbeitet damit.

Im folgenden Bild fragt Thread 2 ab, ob der nächste Knoten freigegeben ist. locked des nächsten Knotens steht auf false. Trotzdem verzweigt die Abfrage, als müsste der Thread warten, bevor er anfangen darf zu arbeiten.

http://images.seelge.de/if_false.png

Irgendwelche Ideen, woran dies liegen könnte?

Entweder ist das setzen des Flags nicht atomar.
Oder das Flag ist nicht köherent in beiden Caches (keyword: volatile)


Nehmt doch einfach Semaphoren ...
BTW: warum nicht einfach ne Queue nehmen, wo die abgearbeitetten Knoten reinwandern?
BTW2: Bist du dir sicher das Parallelierung an dieser Stelle was bringen soll?

Gast
2007-05-31, 21:16:10
Also ich mache es immer so:

#include "process.h"
#include "windows.h"

int main()
{
HANDLE thread1 = (HANDLE)_beginthread(work,0,NULL); //Thread starten

WaitForSingleObject(thread1,INFINITE); //Warten bis Thread fertig ist
}

void work(void *dummy)
{
//Code to execute
}

Alternativ kannst du mit dem Pointer auch Daten übergeben.

P.S.: Ich entwickle immer in Visual Studio 2005. Ob die anderen Entwicklungsumgebungen das auch unterstützen, weiß ich nicht.

del_4901
2007-05-31, 21:20:22
Also ich mache es immer so:

#include "process.h"
#include "windows.h"

int main()
{
HANDLE thread1 = (HANDLE)_beginthread(work,0,NULL); //Thread starten

WaitForSingleObject(thread1,INFINITE); //Warten bis Thread fertig ist
}

void work(void *dummy)
{
//Code to execute
}

Alternativ kannst du mit dem Pointer auch Daten übergeben.

P.S.: Ich entwickle immer in Visual Studio 2005. Ob die anderen Entwicklungsumgebungen das auch unterstützen, weiß ich nicht.

Das bringt ihm gar niks, wenn er parallel arbeiten will. Dann kann ich auch sequenzell das ganze abarbeiten. (da bin ich sogar noch schneller)
Und dann würde ich auch dafür Boost Threads nehmen. Diese besitzen:

Thread.start();
Thread.join(); //blockiert

Und sind zudem noch Plattform unabhänig.

Wenn man parallel bleiben will braucht es Semaphoren oder irgendwelche Buffer bzw. Pipes.

del_4901
2007-05-31, 21:23:55
BTW: Lernt mal bitte Murphys-Gesetz auswendig.

Silpion
2007-05-31, 21:37:42
Entweder ist das setzen des Flags nicht atomar.
Oder das Flag ist nicht köherent in beiden Caches (keyword: volatile)
Hmm... das werde ich morgen als erstes überprüfen. Weitere Tests haben Zustände ergeben, die so eigentlich nicht auftreten sollten.BTW: warum nicht einfach ne Queue nehmen, wo die abgearbeitetten Knoten reinwandern?
BTW2: Bist du dir sicher das Parallelierung an dieser Stelle was bringen soll?Der Geschwindigkeitsunterschied wird nicht gewaltig sein, aber momentan ist die CPU der limitierende Faktor. Zu erreichen versuche ich, dass die HDD die Limitierung ist, daher soll sich ein Thread nur um das Lesen kümmern. Wieviel die Aufteilung bringt, will ich testen. Eine Queue könnte zu groß werden, wenn der erste Thread schneller arbeitet als der zweite. Ein ständiges allokieren/löschen von Knoten möchte ich auch vermeiden, daher verwende ich eine zirkuläre Liste aus drei oder mehr Knoten.

del_4901
2007-05-31, 21:48:11
Hmm... das werde ich morgen als erstes überprüfen. Weitere Tests haben Zustände ergeben, die so eigentlich nicht auftreten sollten.Der Geschwindigkeitsunterschied wird nicht gewaltig sein, aber momentan ist die CPU der limitierende Faktor. Zu erreichen versuche ich, dass die HDD die Limitierung ist, daher soll sich ein Thread nur um das Lesen kümmern. Wieviel die Aufteilung bringt, will ich testen. Eine Queue könnte zu groß werden, wenn der erste Thread schneller arbeitet als der zweite. Ein ständiges allokieren/löschen von Knoten möchte ich auch vermeiden, daher verwende ich eine zirkuläre Liste aus drei oder mehr Knoten.

Schreib dir doch schnell nen eigenen Heap, und überlade den new-operator. [Auf gar keinen Fall den globalen new Operator überladen!!!!!] Solange die Häppchen immer gleich groß bleiben, ist das manchmal ganz praktisch.

BTW: sowas testet man nicht .. sowas überlegt man sich .. aber was erzähle ich .. ich bin warscheinlich trotz meines noch recht jungen Alters oldschool erzogen worden.

BTW2: Memory-Mapped-Files sind möglicherweise keine Lösung für das HDD Problem?

Silpion
2007-06-01, 14:35:12
Hmm... volatile war nicht das Problem. Es hat sich jetzt dadurch gelöst, dass ich den zweiten Thread zu Beginn kurz warten lasse, obwohl dies eigentlich in der Startsituation automatisch der Fall sein sollte.

Memory-Mapped-Files müssen leider entfallen, das Programm muss jeden Teil der Datensätze mehrfach ansehen und es soll schnell gehen.

Selbstverständlich gab es dazu einige Vorüberlegungen, ansonsten hätte ich nicht bereits einen Tag damit verbracht. Arbeitszeit ist kostbar. Ich hatte etwa 50% schneller erwartet, es klappt jetzt und läuft ziemlich genau doppelt so schnell (was natürlich mit unterschiedlichen HDD-CPU-Kombinationen nicht immer so sein wird).

transstilben
2007-06-01, 20:55:04
.... Es hat sich jetzt dadurch gelöst, dass ich den zweiten Thread zu Beginn kurz warten lasse, obwohl dies eigentlich in der Startsituation automatisch der Fall sein sollte.

Hm, "kurz warten" hört sich irgendwie nicht wirklich robust an.
Ich finde den Murphy'schen Ansatz ganz gut :
Wenn es einen Kunden gibt, der eine verdammt schnelle Hardware hat,
und ich gerade tierische Kopfschmerzen habe, ist für diese Hardware
"kurz warten" in seltenen Fällen nicht lang genug ...

HellHorse
2007-06-01, 21:17:26
Hmm... volatile war nicht das Problem. Es hat sich jetzt dadurch gelöst, dass ich den zweiten Thread zu Beginn kurz warten lasse, obwohl dies eigentlich in der Startsituation automatisch der Fall sein sollte.
:cop: :cop: :cop: :cop: :cop: :cop: :cop: :cop:

del_4901
2007-06-02, 02:40:46
Ich hab mir nicht umsonst meinen Kommentar gesparrt!

Aber das mit dem warten ist nicht das erste mal, dass ich sowas höhr ... das scheint der allgemeingültige Lösungsfusch zu sein.

Noch besser war mein Chef neulich: "Jor, wir ham da sonen Arbeitsthread, der nimmt den andern immer zuviel CPU-Zeit weg. Kann man da nicht ein paar sleeps() reinpacken?"

Silpion
2007-06-02, 14:22:43
Kurz warten bedeutet in dem Sinne, dass Thread 1 am Ende seines ersten Arbeitsschrittes Thread 2 aus einer anfänglichen Warteschleife befreit. Es ist gewährleistet, dass sich die Threads nicht gegenseitig überrunden können. Ich mag etwas frisch im Bezug auf Threadprogrammierung sein, aber nicht naiv.

Allerdings scheint es ein Problem mit der Compileroptimierung zu geben. Ohne Optimierung klappt alles prima. Mit Optimierung sind die Änderungen eines Threads für den anderen nicht sichtbar. Alle Variablen, die beide Threads betreffen, sind als volatile markiert und in OpenMP als shared deklariert.

transstilben
2007-06-02, 21:23:14
... Es ist gewährleistet, dass sich die Threads nicht gegenseitig überrunden können ...


Überrunden muß gar nicht sein; manchmal reicht schon ein einfaches Einholen ;)


Allerdings scheint es ein Problem mit der Compileroptimierung zu geben. Ohne Optimierung klappt alles prima. Mit Optimierung sind die Änderungen eines Threads für den anderen nicht sichtbar. Alle Variablen, die beide Threads betreffen, sind als volatile markiert und in OpenMP als shared deklariert.


Ok, aber das heißt doch, daß entweder der Compiler ein Optimierungsproblem für den multithreaded Fall hat (was ich grundsätzlich nicht ausschließen würde),
oder Dein Parallelisierungskonzept noch ein kleines Loch, oder ?

Silpion
2007-06-03, 11:22:54
hmm... ich kann zumindest kein Problem mehr erkennen.

volatile bool writerWait = true;
#pragma omp parallel sections num_threads(2) shared(writerWait)
{
#pragma omp section
{
bool once = true;
for (...)
{
...
if (once)
{
once = false;
writer->locked = true; // Verhindert, dass Thread 1 Thread 2 einholt
writerWait = false; // Befreit Thread 2
}
}
}

#pragma omp section
{
while (writerWait) {;} // Wenn optimiert, kommt Thread 2 hier nie raus
...
}
}Dass der Compiler einen Fehler haben könnte, habe ich bisher nie beachtet, wäre allerdings möglich, immerhin gibt es nicht umsonst ein SP1. Leider konnte ich das bisher noch nicht aufspielen, da unser Projekt Qt 4.2.2 nutzt und sich das nicht mehr mit SP1 kompilieren lässt.

Ah, inzwischen findet man darüber auch offizielle Informationen bei Trolltech und Microsoft und es scheint jetzt einen Hotfix zu geben. Damit kann ich das SP1 installieren.

Ectoplasma
2007-06-03, 12:59:29
@Silipon, ich muss zugeben, dass ich mit OMP nichts gemacht habe. Wenn ich den Code aber richtig interpretiere, dann muss ich dir leider sagen, dass er nicht gut ist.

Gemeint ist speziell der zweite Teil:

#pragma omp section
{
while (writerWait) {;} // Wenn optimiert, kommt Thread 2 hier nie raus
...
}


Das geht ja gar nicht. Du machst ein Polling auf writerWait. Das könnte bei einer SC CPU dazu führen, dass das Thread 1 verhungert. Und zwar dadurch, dass Thread 2 die CPU - Last auf 100% schraubt.

// Wenn optimiert, kommt Thread 2 hier nie raus

Mich wundert das nicht.

Benutze bitte eine Semaphore oder eine Critical Section.

EDIT:

OMP synchonisiert sicherlich den Zugriff auf die Variable "writerWait". Allerdings ist das in deinem Fall nicht der richtige Ansatz.

del_4901
2007-06-03, 13:59:37
Jo, benutzt semaphoren! oder irgendwas anderes atomares wie z.B ein Mutex.

Das da oben ist nämlich ein Vergleich, mind. eine mov-operation und ein Sprungbefehl. Jetzt kannst du dir aussuchen wo Murphy dazwischen haut!

Silpion
2007-06-03, 21:14:22
Hmm... ok, es gibt scheinbar noch eine Menge zu lernen, was ich für parallele for-Schleifen nie benötigte.

Ich habe jetzt critical sections eingebaut, damit funktioniert alles. Für Single-Core wird ein alternativer Pfad benutzt, zumindest bis ich rausgefunden habe, wie ich einen OpenMP-Thread kurz pausieren lassen kann.

del_4901
2007-06-03, 21:31:34
Warten kann man mit sleep. Davon würde ich aber die Finger lassen. Gibt dem einen lieber ne hohe und dem anderen ne niedrige Priorität.

Chris Lux
2007-06-04, 14:01:17
hi,
ich bin auch immer auf der suche nach einer guten portablen thereading lösung. boost::threads und OpenMP hab ich mir schon vor langem schon angesehen. ich würde gern boost::threads nutzen aber die lib ist sehr simpel. sowas wie thread prioritäten kann man da gar nicht setzen. aber sowas wäre für mich schon sehr wichtig. auch bietet boost nur mutexes und barriers zur synchronisation... gibt es da vielleicht noch andere sachen, die ihr empfehlen könnt?

sorry, wenn ich das thema mal so hijacke ;)

muhkuh_rs
2007-06-05, 00:31:56
Mir fällt noch ACE ein.