pthread-Problem

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

pthread-Problem

Beitrag von hundvdf »

Hallo,
ich habe mir für meinen Raytracer eine kleine Thread-Klasse geschrieben, um die Strahlen parallel berechnen zu lassen. Allerdings habe ich das Problem, dass pthread_create irgendwann nichtmehr aufgerufen wird. Muss ich da irgendwas wieder freigeben? Ich habe mich da an http://www.linuxselfhelp.com/HOWTO/C++P ... TO-18.html orientiert.
Meine Thread-Klasse sieht so aus:

Code: Alles auswählen

class Thread {
public:
	Thread();
	Thread(Szene *szene);
	virtual ~Thread();

	void Run(unsigned int x,unsigned int y);

	bool finished() { return this->m_finished; }

	FARBE GetFarbe() const { return this->m_farbe; }
	int GetX() const { return this->m_x; }
	int GetY() const { return this->m_y; }

	void *run();
private:
	Szene *m_szene;
	bool m_finished;
	unsigned int m_x;
	unsigned int m_y;
	FARBE m_farbe;

	pthread_t m_thread;
};

Thread::Thread() {
	this->m_x=0;
	this->m_y=0;
	this->m_finished=true;
	this->m_farbe=FARBE(0,0,0);
	this->m_szene=NULL;
	this->m_mutex=PTHREAD_MUTEX_INITIALIZER;
}

Thread::Thread(Szene *szene) {
	this->m_x=0;
	this->m_y=0;
	this->m_finished=true;
	this->m_farbe=FARBE(0,0,0);
	this->m_szene=szene;
	this->m_mutex=PTHREAD_MUTEX_INITIALIZER;
}

Thread::~Thread() {
	this->m_x=0;
	this->m_y=0;
	this->m_finished=true;
	this->m_farbe=FARBE(0,0,0);
	this->m_szene=NULL;
}

void *runthread(void *thread) {
	((Thread*)thread)->Run();
	return 0;
}

void Thread::Run(unsigned int x,unsigned int y) {
	if(this->m_szene==NULL)
		throw new Exception(NULL,__FILE__,__LINE__,"Fehler","Szene im Thread nicht gesetzt!");

	this->m_x=x;
	this->m_y=y;

	this->m_finished=false;

	pthread_create(&this->m_thread,NULL,runthread,(void*)this);
}

void *Thread::run() {
	this->m_farbe=this->m_szene->CalcRay(this->m_x,this->m_y);
	this->m_finished=true;
	return 0;
}
Ich habe jetzt z.B. 4 Thread-Objekte in der Main-Schleife:

Code: Alles auswählen

		Thread *threads = new Thread[maximal_threads];
		for(unsigned int i=0;i<maximal_threads;i++) {
			threads[i] = Thread(szene);
		}
Und für die Berechnung hab ich diese Schleifen:

Code: Alles auswählen

				for(unsigned int y=0;y<screen_height;y++) {
					for(unsigned int x=0;x<screen_width;x++) {
						bool thread_found=false;
						while(!thread_found) {
							for(unsigned int t=0;t<maximal_threads;t++) {
								if(threads[t].finished()) {
									color[threads[t].GetX()][threads[t].GetY()]=threads[t].GetFarbe();
									threads[t]=Thread(szene);
									threads[t].Run(x,y);
									thread_found=true;
									break;
								}
							}
							if(thread_found)
								break;

							usleep(100);
						}
					}
				}
So, jetzt mein Problem: wenn ich mit einer Auflösung vom 100x100 die Szene berechnen lasse funktioniert alles, setze ich die Auflösung auf 200x200, hängt er in der 163. Zeile beim 71. Pixel fest und geht nicht mehr weiter. Die Run(x,y)-Methode wird auch danach noch maximal_threads oft aufgerufen, aber es scheint, als ob er pthread_create nichtmehr richtig aufrufen würde...
Das sind meine ersten Gehversuche mit Threads in C(++), hab nurmal beim Studium Threads mit Java gemacht (aber da natürlich auch nicht so viele).

Ich hoffe, irgendjemand findet den Fehler und kann mir helfen.

Vielen Dank schonmal im Voraus
Grüße
hundvdf
kristof
Beiträge: 92
Registriert: 19.01.2009, 13:05

Re: pthread-Problem

Beitrag von kristof »

Du erstellst momentan für jeden Pixel einen neuen Thread. Das Erzeugen eines Threads ist aber eine sehr zeitaufwendige Sache. Versuche lieber einen Pool von Threads einmal zu Beginn des Programms zu erstellen. In jedem Thread könnte dann eine Schleife laufen, die einen Pixel nach dem anderen berechnet. Das wird dann natürlich komplizierter, da mehrere Threads auf die gleichen Daten zugreifen.
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

kristof hat geschrieben:Du erstellst momentan für jeden Pixel einen neuen Thread. Das Erzeugen eines Threads ist aber eine sehr zeitaufwendige Sache.
Ja, aber zur Zeit ist dieser Zeitaufwand für das Erstellen der Threads deutlich geringer als mein noch unoptimiertes Raytracing-Verfahren ;-)
kristof hat geschrieben:Versuche lieber einen Pool von Threads einmal zu Beginn des Programms zu erstellen. In jedem Thread könnte dann eine Schleife laufen, die einen Pixel nach dem anderen berechnet. Das wird dann natürlich komplizierter, da mehrere Threads auf die gleichen Daten zugreifen.
Hm, könnte ich natürlich machen. Eine andere Idee, die mir gerade noch gekommen ist, aber leider nicht funktioniert:

ich erstelle am Anfang die Threads und übergebe denen dann nach und nach die Pixel die berechnet werden sollen:

Code: Alles auswählen

void Thread::Run(unsigned int x,unsigned int y) {
    this->m_x=x;
    this->m_y=y;
    this->m_finished=false;
}

void *Thread::run() {
    while(!this->m_done) {
        if(!this->m_finished) {
            this->m_szene->CalcRay();
            this->m_finished=true;
        }
        else {
            usleep(100);
        }
    }
}
leider funktioniert es so nicht, da die Daten, die ich in der Run(x,y)-Methode in die Klasse eingebe, nicht in der run()-Methode, also im Thread, ankommen. Warum ist denn das so? Also prinzipiell würde ich denken, das Thread-Objekt ist universal und nur die run()-Methode läuft in einem zusätzlichen Thread, die aber auch auf die gleichen Daten, also den Klassen-Membern, zugreifen kann...

Aber unabhängig davon: woran liegt es denn, dass ich bei meinem ursprünglichen Code die Threads nicht mehr erzeugen kann? Gibt es eine Maximalzahl der Threads die ein Programm anlegen darf?

Danke
hundvdf
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: pthread-Problem

Beitrag von kimmi »

Such mal nach dem Master-Worker-Pattern und minimiere die Anzahl der laufenden Threads. Irgendwann wird das ineffizient, bei der Anzahl, die du da generierst, definitiv.

Gruß Kimmi
hagbard
Beiträge: 66
Registriert: 05.08.2010, 23:54

Re: pthread-Problem

Beitrag von hagbard »

Also nach meiner unwissenschaftlichen empirischen Erkenntnis sollte ab 200 Threads unter Windows Schluss mit lustig sein. :mrgreen:
Wenn dein Code zudem sehr rechentintensiv ist sollte die Anzahl der Threads nicht deutlich größer als die Anzahl der erwarteten Prozessorkerne sein.
kristof
Beiträge: 92
Registriert: 19.01.2009, 13:05

Re: pthread-Problem

Beitrag von kristof »

hundvdf hat geschrieben: Aber unabhängig davon: woran liegt es denn, dass ich bei meinem ursprünglichen Code die Threads nicht mehr erzeugen kann? Gibt es eine Maximalzahl der Threads die ein Programm anlegen darf?
Das würde mich nicht wundern, wenn es eine Obergrenze für Threads gibt. Du erzeugst hier mehrere zehn tausend in kürzester Zeit. Dafür sind Threads aber nicht erfunden worden.
Um das ganze einfacher zu halten könntest du das Bild auch am Anfang einfach schon auf die erzeugten Threads aufteilen. Bei vier Threads übernimmt jeder Thread ein viertel des Bildes. Dann brauchst du dir nicht so viele Gedanken über kritische Bereiche zu machen.
Und du könntest dir auch mal den Rückgabewert von pthread_create ansehen. Vielleicht gibt das schon Aufschluss darüber wo das Problem liegt.
So wie du das jetzt machst, dass jeder Pixel seinen eigenen Thread hat wäre es sicher schneller das ganze sequentiell zu lösen. Zudem profitierst du auch nur so lange von Threads, wie dein Rechner mindestens so viele Kerne hat.
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

kristof hat geschrieben:Das würde mich nicht wundern, wenn es eine Obergrenze für Threads gibt. Du erzeugst hier mehrere zehn tausend in kürzester Zeit. Dafür sind Threads aber nicht erfunden worden.
So stimmt das nicht, ich habe ja immer maximal 2/4/8 Threads gleichzeitig laufen (Zahl lässt sich über die Konstante maximal_threads einstellen). Sobald diese Zahl erreicht ist, wird gewartet, bis ein Thread fertig ist und erst danach wird ein neuer gestartet.
kristof hat geschrieben:Um das ganze einfacher zu halten könntest du das Bild auch am Anfang einfach schon auf die erzeugten Threads aufteilen. Bei vier Threads übernimmt jeder Thread ein viertel des Bildes. Dann brauchst du dir nicht so viele Gedanken über kritische Bereiche zu machen.
Das habe ich mir auch schon überlegt. Das Problem dabei ist, dass evtl. in einem Viertel relativ wenige Objekte sind und somit die Pixel schneller berechnet werden können. Im Extremfall ist die ganze Geometrie in einem Viertel drin, und durch die Aufteilung in 4 Gleichmäßige Viertel würde dann gar nichts bringen. Deshalb wollte ich jeden Pixel separat berechnen.
hagbard hat geschrieben:Also nach meiner unwissenschaftlichen empirischen Erkenntnis sollte ab 200 Threads unter Windows Schluss mit lustig sein. :mrgreen:
Wenn dein Code zudem sehr rechentintensiv ist sollte die Anzahl der Threads nicht deutlich größer als die Anzahl der erwarteten Prozessorkerne sein.
Also ich arbeite ja nicht unter Windows ;-) aber wie ich oben schon geschrieben habe: es laufen ja gleichzeitig immer nur 4 Worker-Threads (auch wenn ich auf 1 Worker-Thread runtergehe funktioniert es nicht) und wenn ein Thread beendet ist, sollte es doch eigentlich kein Problem sein, einen neuen anzulegen. Und ich denke mal, 4 Worker-Threads bei einem Dual-Core-PC sind doch in Ordnung (denn das Programm läuft evtl. auch noch auf einem Quad-Core).
kristof hat geschrieben:Und du könntest dir auch mal den Rückgabewert von pthread_create ansehen. Vielleicht gibt das schon Aufschluss darüber wo das Problem liegt.
Hm, warum mache ich eigentlich das naheliegenste nicht als erstes... Hab hierbei die Fehlernummer 12 bekommen: Cannot allocate memory
Daraufhin hab ich nochmal im Internet geschaut und bin auf diese Seite gestoßen: http://www.ijon.de/comp/tutorials/threads/create.html:
Normalerweise bleibt, wenn ein Thread sich beendet hat, sein Identifier weiterhin gültig und er belegt auch Ressourcen im Rechner. [...]
Manchmal ist dies nicht mehr nötig: Der Rückgabewert wird nicht mehr gebraucht und es können alle Ressourcen, die ein Thread belegt, wieder freigegeben werden. Mit Hilfe des Befehls

Code: Alles auswählen

int pthread_detach (pthread_t th);
teilt man einem Thread mit, daß nach seiner Beendigung sämtliche Ressourcen freigegeben werden können.
oder eben den Thread direkt als detached anlegen, so dass keine Ressourcen gespeichert bleiben...

Und wenn ich das so einbaue, dann funktioniert das Ganze auch. Zum Vergleich mal die Werte:
1 Thread: ca. 80sec
2 Threads: ca. 30sec
4 Threads: ca. 20sec
8 Threads: ca. 7sec
16 Threads: ca. 6.5sec
danach wirds eher wieder etwas langsamer. (Angaben beziehen sich auf meinem AMD Turion II Dual-Core Mobile M520, 2x2,3GHz)
Das ist auch bisher erst eine erste Version des Raytracers, und deshalb absolut unoptimiert, deshalb wollte ich zumindest mal Threads einbauen, damit es wenigstens etwas schneller geht und ich nicht bei jedem Test mehrere Minuten warten muss... Und eben die Funktion zum Freigeben des Thread-Speichers habe ich gesucht und zuerst nicht gefunden...

Auf jeden Fall vielen Dank an alle die mitgedacht haben!

Grüße
hundvdf
kristof
Beiträge: 92
Registriert: 19.01.2009, 13:05

Re: pthread-Problem

Beitrag von kristof »

hundvdf hat geschrieben: So stimmt das nicht, ich habe ja immer maximal 2/4/8 Threads gleichzeitig laufen (Zahl lässt sich über die Konstante maximal_threads einstellen). Sobald diese Zahl erreicht ist, wird gewartet, bis ein Thread fertig ist und erst danach wird ein neuer gestartet.
Ich bezog mich dabei auf das Erzeugen der Threads. Aber schön das es jetzt funktioniert. Trotz allem rate ich dir, gleich als nächstes zu versuchen die pthread_create Aufrufe zu minimieren ;) Dann läuft das ganze sicher in unter einer Sekunde.
Viel Spass noch ;)
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: pthread-Problem

Beitrag von kimmi »

Irgendwann wird der Kontext-Switch zum Flaschenhals, daher der Einbruch bei > 16 Threads. Interessant wäre sicherlich auch die Cache-Misses, durch cache-optimierte Algorithmen solltest du da einiges rausholen können.

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: pthread-Problem

Beitrag von Krishty »

Ich will nochmal erwähnen, das
du existierst 163x200+71 == ziemujuch nah an 32768 == 2^15 == short int max Threads
Wenn es um eine Zweierlpotenz rum krach tkannst du dir sicer sein, dass du die API missbrauchst.
(Das HANDLE-limite der WinAPI liegt auch iwo in dem Bereich. Iwie dachten sich ALLE, dass diese Größenordnung genug ist. Von allem bis auf Pixel.)

Jetrz interessiert mich aber, warum es bei 16 Threads schneller ist als bei 4 (der Kernanzahl). Haben Athlons HyperThreading oder was
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

Krishty hat geschrieben:Ich will nochmal erwähnen, das
du existierst 163x200+71 == ziemujuch nah an 32768 == 2^15 == short int max Threads
Wenn es um eine Zweierlpotenz rum krach tkannst du dir sicer sein, dass du die API missbrauchst.
(Das HANDLE-limite der WinAPI liegt auch iwo in dem Bereich. Iwie dachten sich ALLE, dass diese Größenordnung genug ist. Von allem bis auf Pixel.)
Wie oben ja schon geschrieben, jetzt nachdem ich die Threads wieder ordnungsgemäß freigegeben habe, funktioniert es ja. Weil das ja nahe bei ner 2er-Potenz lag hab ich ja sowas schon vermutet, konnte aber zuerst nichts finden im Netz.
Krishty hat geschrieben:Jetzt interessiert mich aber, warum es bei 16 Threads schneller ist als bei 4 (der Kernanzahl). Haben Athlons HyperThreading oder was
Das ist eine gute Frage, zumal ich auch nur einen DualCore-Prozessor hab. Ich frage mich auch, warum er mit 8 Threads nichmal ein Zehntel der Zeit braucht (bzw. bei 2 Threads auch weniger als die Hälfte) Ich würde denken, dass eben bei 2 Threads 2 Pixel gleichzeitig berechnet werden und deshalb so in etwas die halbe Zeit benötigt wird. Das ist ja schon ein wirklich deutlicher Unterschied...
kimmi hat geschrieben:Irgendwann wird der Kontext-Switch zum Flaschenhals, daher der Einbruch bei > 16 Threads. Interessant wäre sicherlich auch die Cache-Misses, durch cache-optimierte Algorithmen solltest du da einiges rausholen können.

Gruß Kimmi
Danke für den Tipp. Wie gesagt, das sollte erstmal ein erster Ansatz sein, da auch das Raytracing noch überhaupt nicht optimiert ist. Das kommt dann alles im nächsten Schritt ;-)
kristof hat geschrieben:Ich bezog mich dabei auf das Erzeugen der Threads. Aber schön das es jetzt funktioniert. Trotz allem rate ich dir, gleich als nächstes zu versuchen die pthread_create Aufrufe zu minimieren ;) Dann läuft das ganze sicher in unter einer Sekunde.
Viel Spass noch ;)
Meinst du echt, dass das so viel ausmacht? Ich habs ja schon ausprobiert einen Thread zu starten und dann die Daten des zugehörigen Klassenobjekts zu ändern, aber irgendwie kommen die Daten nicht im Thread an, bzw. es scheint, als wäre das Thread-Objekt plötzlich eine Kopie des ursprünglichen. Ich hab das so versucht:

Code: Alles auswählen

class Thread {
public:
Thread();
~Thread();
static void *RunThread(void*thread) { return ((*Thread)thread)->Run(); }
void *Run();
void End() { this->m_done=true; }
private:
bool m_done;
};

Thread::Thread() {
this->m_done=false;
pthread_create(RunThread,this);
}

void Thread::Run() {
while(!this->m_done) {
[TU IRGENDWAS]
}
cout << "Thread beendet!" << endl;
}

main() {
Thread t; // Thread wird gestartet und läuft auch
t.End(); // Funktioniert nicht mehr
}
Warum funktioniert dieser Code denn nicht? Ich übergebe dem Thread ja das Objekt und sollte doch anschließend auch "von außen" Werte ändern können, da es ja das selbe Objekt ist...

Grüße
hundvdf
kristof
Beiträge: 92
Registriert: 19.01.2009, 13:05

Re: pthread-Problem

Beitrag von kristof »

hundvdf hat geschrieben: Meinst du echt, dass das so viel ausmacht? Ich habs ja schon ausprobiert einen Thread zu starten und dann die Daten des zugehörigen Klassenobjekts zu ändern, aber irgendwie kommen die Daten nicht im Thread an, bzw. es scheint, als wäre das Thread-Objekt plötzlich eine Kopie des ursprünglichen. Ich hab das so versucht:

Code: Alles auswählen

class Thread {
public:
Thread();
~Thread();
static void *RunThread(void*thread) { return ((*Thread)thread)->Run(); }
void *Run();
void End() { this->m_done=true; }
private:
bool m_done;
};

Thread::Thread() {
this->m_done=false;
pthread_create(RunThread,this);
}

void Thread::Run() {
while(!this->m_done) {
[TU IRGENDWAS]
}
cout << "Thread beendet!" << endl;
}

main() {
Thread t; // Thread wird gestartet und läuft auch
t.End(); // Funktioniert nicht mehr
}
Warum funktioniert dieser Code denn nicht? Ich übergebe dem Thread ja das Objekt und sollte doch anschließend auch "von außen" Werte ändern können, da es ja das selbe Objekt ist...
Naja, Wie Krishty schon sagte, du missbrauchst hier die API. Threads bringen verhältnismäßig wenig, wenn sie nur so kurz existieren, da die Zeit, die das Betriebssystem braucht um einen zu erzeugen im Verhältnis zur Laufzeit noch recht gross ist. Ich könnte mir auch vorstellen, dass das die Zeiten verfälscht die du da gemessen hast, denn die machen so eigentlich keinen Sinn.
Versuche in dem Code mal ein volatile vor bool m_done; zu schreiben. Das sagt dem Compiler, dass er keine Optimierungen auf m_done anwenden soll. Diese Optimierungen sorgen manchmal dafür, dass die Variable nicht immer aus dem Speicher geholt werden muss und statt dessen in einem schneller zu erreichenden Register liegt. Das willst du hier aber nicht, da du dann nicht erfährst ob ein anderer Thread die Variable geändert hat. (Alle Angaben wie immer ohne Gewähr)
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

Oh, mist, mein Fehler...
Hatte ja die Thread-Objekte wie oben beschrieben angelegt. Allerdings hab ich dabei jedes Objekt doppelt angelegt (also einmal mit new und direkt danach nochmal den Konstruktor aufgerufen). Beim zweiten Anlegen wurde auch gleich wieder der Destruktor aufgerufen und der Thread somit beendet...

Die neuen Werte:
1 Thread: 80sec
2 Threads: 30sec
4 Threads: 15sec
8 Threads: 7sec
16 Threads: 6sec
32 Threads: 5,5sec
und danach gehts auch wieder hoch. Also so viel scheint das doch nicht auszumachen.

Vielen Dank
hundvdf
Benutzeravatar
Schrompf
Moderator
Beiträge: 4878
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: pthread-Problem

Beitrag von Schrompf »

Und Du erzeugst wirklich Kopien der Threads, wenn ich Deinen Code oben richtig verstanden habe:

Code: Alles auswählen

Thread *threads = new Thread[maximal_threads];
for(unsigned int i=0;i<maximal_threads;i++) {
threads[i] = Thread(szene);
}
Man beachte die Zuweisung. Ich kann nur wiederholen, was die anderen schon gesagt haben: Unterscheide zwischen Threads und Aufgaben. Lege Dir ein paar Workerthreads an und verwende sie wieder, um Stück für Stück alle Aufgaben abzuarbeiten. Jeder Pixel ist eine Aufgabe.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

kristof hat geschrieben:Naja, Wie Krishty schon sagte, du missbrauchst hier die API. Threads bringen verhältnismäßig wenig, wenn sie nur so kurz existieren, da die Zeit, die das Betriebssystem braucht um einen zu erzeugen im Verhältnis zur Laufzeit noch recht gross ist. Ich könnte mir auch vorstellen, dass das die Zeiten verfälscht die du da gemessen hast, denn die machen so eigentlich keinen Sinn.
Die Zeiten hab ich gemessen von Anfang der Berechnungen bis das Bild komplett berechnet ist. Ich habs ja jetzt auch umgeändert (s. Post oben), so dass wirklich nur maximal_threads Threads durchgehend laufen und diese ständig neue Daten bekommen.
kristof hat geschrieben:Versuche in dem Code mal ein volatile vor bool m_done; zu schreiben. Das sagt dem Compiler, dass er keine Optimierungen auf m_done anwenden soll. Diese Optimierungen sorgen manchmal dafür, dass die Variable nicht immer aus dem Speicher geholt werden muss und statt dessen in einem schneller zu erreichenden Register liegt. Das willst du hier aber nicht, da du dann nicht erfährst ob ein anderer Thread die Variable geändert hat. (Alle Angaben wie immer ohne Gewähr)
Naja, optimiert wird da bisher durch den Compiler noch gar nichts, gerade läuft alles noch im Debug-Modus, d.h. Optimierungen sind komplett deaktiviert.

Grüße
hundvdf
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

Schrompf hat geschrieben:Und Du erzeugst wirklich Kopien der Threads, wenn ich Deinen Code oben richtig verstanden habe:

Code: Alles auswählen

Thread *threads = new Thread[maximal_threads];
for(unsigned int i=0;i<maximal_threads;i++) {
threads[i] = Thread(szene);
}
Man beachte die Zuweisung. Ich kann nur wiederholen, was die anderen schon gesagt haben: Unterscheide zwischen Threads und Aufgaben. Lege Dir ein paar Workerthreads an und verwende sie wieder, um Stück für Stück alle Aufgaben abzuarbeiten. Jeder Pixel ist eine Aufgabe.
Hab ich ja so auch gemacht. Das Problem war eben wie oben beschrieben, dass ich die Threads doppelt angelegt habe und der Destruktor dann aufgerufen wurde, wodurch meine Worker-Threads direkt wieder beendet wurden.
kristof
Beiträge: 92
Registriert: 19.01.2009, 13:05

Re: pthread-Problem

Beitrag von kristof »

Die Werte ergeben für mich immer noch keinen Sinn. Du hast ja keine 32 CPU Kerne. Wie sieht denn jetzt der Teil aus, in dem du den Threads die zu berechnenden Pixel zuweist?
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

kristof hat geschrieben:Die Werte ergeben für mich immer noch keinen Sinn. Du hast ja keine 32 CPU Kerne. Wie sieht denn jetzt der Teil aus, in dem du den Threads die zu berechnenden Pixel zuweist?
Nein, ich hab leider nur 2 Kerne ;-)
Warum machen denn für dich diese Werte keinen Sinn? Weil es auch mit mehr als 2(=Kerne) Threads noch deutlich kürzer wird? Das hat mich auch schon gewundert, ich weiß, dass 1-2 Threads mehr als Kerne noch etwas mehr bringen können, aber bei mir scheint es danach ja auch nochmal deutlich schneller zu werden.

Code: Alles auswählen

void *Thread::Run() {
while(!this->m_done) {
if(!this->m_finished) {
this->m_farbe=this->szene->CalcRay(this->m_x,this->m_y);
this->m_finished=true;
}
else {
usleep(100)
}
}
}

void Thread::Start(int x,int y) {
this->m_x=x;
this->m_y=y;
this->m_finished=false;
}

bool Thread:Finished() {
return this->m_finished;
}

main() {
[...]
for(THREADS) {
if(threads[i].Finished()) {
threads[i].Start(x,y);
}
}
}
PS: Warum kann man denn den Code hier nicht einrücken zur besseren Lesbarkeit? Alle Tabulatoren und Leerzeichen am Anfang der Zeile gehen verloren...
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: pthread-Problem

Beitrag von Krishty »

Ich tippe mal ganz galant darauf, dass usleep(100) mehr Zeit in Anspruch nimmt als das Ausrechnen von 30 Pixeln. Und wenn du nur zwei Threads erzeugst, verbringen die 95 % ihrer Lebenszeit mit Warten (3 ms Pixel berechnen – 100 ms Pause – 3 ms Pixel berechnen – 100 ms Pause – …). Wenn du hingegen 32 Threads erzeugst, rechnen die letzten 2 weiter, während die vorderen 30 pausieren. Und wenn sie fertig sind, sind die vorderen zwei zufällig genau mit Warten fertig und übernehmen die Rechenzeit wieder indem sie ihre nächsten Pixel berechnen.

Das Verhältnis von Wartezeit zu Rechenzeit ist eben genau so, dass bei 32 Threads (bzw. irgendwo in dem Bereich 25–50) immer nur 2 gleichzeitig aktiv sind – was deine CPU zufällig optimal auslastet, aber sonst nicht erstrebenswert ist.

Was die Formatierung angeht: Benutz [ code=cpp ]
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: pthread-Problem

Beitrag von kimmi »

Signalisiere den Threads doch einfach, dass sie warten beziehungsweise aufwachen sollen. So etwas wie eine Condition sollte es doch auch mit pthread geben.

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: pthread-Problem

Beitrag von Krishty »

Er sollte besser dafür sorgen, dass sie immer sofort mit Arbeit gefüttert werden – entweder, indem das Warten komplett durch hochfrequentes Abfragen des Signals geschieht (wie nennt man das? Heißes Warten oder so?), oder – besser – durch was „yield“-mäßigs, vielleicht ein Unix-Gegenstück zu SwitchToThread() und mehr als eine einzelne Pixelzuweisung (zehn auf einmal oder so, schließlich gibt es noch einen dritten Thread, der die Arbeit verteilt, und jedes Mal Umschalten erfordert, bevor eine neue Aufgabe angefangen werden kann). Denn eigentlich sollten die Threads ja überhaupt nicht warten – er will schließlich darauf hinaus, dass er so viele Threads wie CPU-Kerne hat und die so schnell wie möglich fertig werden; alles andere wäre Verschwendung von Rechenpotential.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: pthread-Problem

Beitrag von kimmi »

Du kannst in der Regel Threads in einen Waiting-State versetzen, so sie dann auch nichts tun. Das ist für einen Threadpool beziehungsweise für Workerthreads wie zum Beispiel einem im Hintergrund laufenden IO-Thread recht üblich. Die warten dann da und verbraten keine CPU-Zeit.
Und so wie ich das sehe, hat er sich einen Threadpool angelegt. Klar kann man Threads auch wieder zerstören, aber das ständige Zerstören und neu kreieren ist teurer als ein Threadpool. Und Threadpools sind für solche AUfgaben eigentlich die Regel, zumindest für reine Workerthreads.

Unter Windows macht man das zum Beispiel mit WaitForSingleObject, unter boost::thread gibt es eine Condition-Klasse.

Siehe auch http://en.wikipedia.org/wiki/Thread_pool_pattern

Gruß Kimmi
Zuletzt geändert von kimmi am 07.04.2011, 18:19, insgesamt 1-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: pthread-Problem

Beitrag von Krishty »

Er hat sich einen Thread-Pool angelegt, um die CPU möglichst hoch auszulasten. Genau. Und jeder dieser Threads läuft in einer Dauerschleife, in der er einen Pixel entgegennimmt, ausrechnet, und auf den nächsten wartet, bis alle Pixel verarbeitet sind. Dann zerstört er die Threads.

Die Namen Start() und End() erzeugen und zerstören nicht tatsächlich Threads, sondern setzen nur die Signale, ob der Thread noch am rechnen ist oder einen neuen Pixel braucht.

Oder reden wir aneinander vorbei? :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: pthread-Problem

Beitrag von kimmi »

Ich glaube ja. Allerdings ist die Frage, ob die Threads tatsächlich zerstört werden? Wenn ja, warum? Wenn nein: ich hab nichts gesagt :)...
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: pthread-Problem

Beitrag von Krishty »

kimmi hat geschrieben:Allerdings ist die Frage, ob die Threads tatsächlich zerstört werden?
So lange wir keinen vollständigen Quelltext haben, wissen wir das nicht. Aber ist ein guter Punkt; das Zerstören sollte er auf keinen Fall vergessen.

Weiter: das Thread Pool Pattern implementiert er. Nur hatten wir das Phänomen, dass ein viel zu großer Pool bei ihm schneller war als ein passend großer. Die Ursache dafür haben wir (hoffentlich!) identifiziert; das war ein usleep(100); in der Hauptschleife seiner Threads. Ich glaube nicht, dass die da aus einem speziellen Grund drin war, sondern unterstelle einfach mal, dass der OP es nicht besser wusste.

Das nächste Problem sehe ich darin, dass die Aufgabenliste jedes Threads nur ein Element lang ist. Aber damit befasse ich mich, wenn das mit der Höchstleistung bei 32 Threads geklärt ist.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

Krishty hat geschrieben:
kimmi hat geschrieben:Allerdings ist die Frage, ob die Threads tatsächlich zerstört werden?
So lange wir keinen vollständigen Quelltext haben, wissen wir das nicht. Aber ist ein guter Punkt; das Zerstören sollte er auf keinen Fall vergessen.
Also ich hab jetzt einen Threadpool von z.B. 8 Worker-Threads die beim Programmstart gestartet werden und in die Dauerschleife gehen. Sobald die Threads Daten bekommen zum berechnen, werden diese auch berechnet. Beim Programmende rufe ich den Destruktor auf, der zum einen die Dauerschleife stoppt (m_done=true) und zum anderen auch den Thread zerstört (mit pthread_detach)
Also dazwischen werden keine Threads mehr erzeugt oder zerstört, so wie das am Anfang der Fall war!
Krishty hat geschrieben:Weiter: das Thread Pool Pattern implementiert er. Nur hatten wir das Phänomen, dass ein viel zu großer Pool bei ihm schneller war als ein passend großer. Die Ursache dafür haben wir (hoffentlich!) identifiziert; das war ein usleep(100); in der Hauptschleife seiner Threads. Ich glaube nicht, dass die da aus einem speziellen Grund drin war, sondern unterstelle einfach mal, dass der OP es nicht besser wusste.
Das usleep(100) habe ich drin, damit die Threads nicht durchgehend volle Rechenleistung brauchen, auch wenn sie gar nichts zu berechnen haben (ist ja ansonsten ähnlich wie eine while(true)-Schleife in der nichts gemacht wird, die lastet das System ja auch aus (oder ist das in einem Thread anders? Habs nicht wirnkich ausprobiert). Und nebenbei bemerkt: usleep(100) wartet nicht 100ms sondern nur 100µs! Und das ist nur ein sehr kleiner Bruchteil von der Zeit die für die Berechnung von 1 Pixel benötigt wird!
Krishty hat geschrieben:Das nächste Problem sehe ich darin, dass die Aufgabenliste jedes Threads nur ein Element lang ist. Aber damit befasse ich mich, wenn das mit der Höchstleistung bei 32 Threads geklärt ist.
Meinst du damit, dass ich den nächsten Pixel zum Berechnen erst dem Thread übergebe, wenn er den alten abgearbeitet hat? Könnte ich natürlich auch anders machen, dass jeder Thread einen Pool mit 10 Pixeln bekommt und dann stetig wieder nachfüllen.

Zum anderen gibt es bei den pthreads auch Conditions, aber wie gesagt, das ist jetzt meine erste größere Erfahrung mit Threads (mit C++), hab da schon was über Conditions gelesen, war aber noch zu faul um mir das richtig durchzulesen :D (sollte einfach schnell ne Möglichkeit geben, das Raytracing kurzzeitig zu beschleunigen)
Wenn alles andere fertig ist und auch funktioniert, werd ich mich nochmal an die Threads machen, diese etwas zu optimieren mit den Conditions und Pixel-Pools.

Grüße
hundvdf
Benutzeravatar
Schrompf
Moderator
Beiträge: 4878
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: pthread-Problem

Beitrag von Schrompf »

Der wartet keine 100 Mikrosekunden, sondern mindestens 100 Mikrosekunden. Bei hoher Systemlast wartet der bis zu einem Time Slice - 5ms unter Vista und Win7, wenn ich mich recht erinnere.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

Schrompf hat geschrieben:Der wartet keine 100 Mikrosekunden, sondern mindestens 100 Mikrosekunden. Bei hoher Systemlast wartet der bis zu einem Time Slice - 5ms unter Vista und Win7, wenn ich mich recht erinnere.
Ja, das stimmt natürlich, aber es sind eben keine 100 Millisekunden. Aber wenn ich das weglasse, dann verbrauchen auch die nicht-rechnenden Threads durch die Endlosschleife Rechenleistung und das ganze wird sogar noch langsamer. Also im Endeffekt müsste ich einen Thread wirklich schlafen legen und dann mit einer Condition wieder aufwecken wenn es neue Daten gibt (oder eben ein Pixel-Pool erstellen, damit er dauerhaft was zu rechnen hat). Aber kann ein per Condition aufgeweckter Thread denn auch schon vor dem nächsten Time-Slice anfangen zu rechnen, oder wird der auch erst mit dem Time-Slice gestartet? Weiß das jemand, wie da die unterschiedlichen Betriebssysteme das Ganze handhaben?

Grüße
hundvdf
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: pthread-Problem

Beitrag von kimmi »

Verbrauchen die Threads, wenn du wartest, mit dem Funktionsaufruf pthread_cond_wait ebenfalls CPU-Zeit? Wird dann auch wirklich gewartet? Das erscheint mir suspekt.
Und ich kann dir leider nicht sagen, wie das Verhalten von POSIX-Threads hier sind. Ich vermute mal, wenn der Scheduler das für sinnvoll hält, kommen sie in diesem Slice drann, sonst später. In einem Realtime-OS kannst du davon ausgehen, dass so etwas augenblicklich verarbeitet wird. Debian-Linux für einen PC bietet das standardgemäss nicht an. Da fehlt diese Garantie.

Gruß Kimmi
hundvdf
Beiträge: 28
Registriert: 03.10.2002, 13:48
Kontaktdaten:

Re: pthread-Problem

Beitrag von hundvdf »

kimmi hat geschrieben:Verbrauchen die Threads, wenn du wartest, mit dem Funktionsaufruf pthread_cond_wait ebenfalls CPU-Zeit? Wird dann auch wirklich gewartet? Das erscheint mir suspekt.
Vermutlich verbrauchen die dann keine CPU-Zeit. Denn wie gesagt: ich war bisher noch zu faul, mich bei den Conditions einzulesen :D Deshalb wird da nix pausiert, und genau deshalb hab ich auch das usleep da drin
kimmi hat geschrieben:Und ich kann dir leider nicht sagen, wie das Verhalten von POSIX-Threads hier sind. Ich vermute mal, wenn der Scheduler das für sinnvoll hält, kommen sie in diesem Slice drann, sonst später.
Ja, und was ich gerade bemerkt habe: SDL bietet auch Threads an. Und da ich sowieso SDL benutze um das berechnete Bild dann anzuzeigen, und da das Programm auch auf Linux und Windows-Rechnern laufen soll, werd ich mir mal die SDL-Threads näher anschauen, dann muss man unter Windows nicht nochmal eine zusätzliche Bibliothek mit einbinden. Auf den ersten Blick scheinen die SDL_Threads ziemlich ähnlich wie die POSIX-Threads zu funktionieren (also was den Aufruf, Start, Ende,... angeht)
kimmi hat geschrieben:In einem Realtime-OS kannst du davon ausgehen, dass so etwas augenblicklich verarbeitet wird. Debian-Linux für einen PC bietet das standardgemäss nicht an. Da fehlt diese Garantie.
Also Realtime brauch ich für den Raytracer ja nicht. Es hätte mich nur so allgemein interessiert, wie das mit aufgeweckten Threads ist, ob die dann sofort gestartet werden oer eben auch gewartet wird bis Rechenleistung "frei" wird.
Antworten