# Optimierung durch Threads



## Highchiller (23. Jan 2013)

Hallo Leute,

in der Uni arbeiten wir grad an verschiedensten Projekten. Es geht weitestgehend um Visualisierungen, in meinem Fall um Wellenfunktionen. Wobei wir damit direkt zu meinem Problem kommen.

Damit ich noch nebenbei weiter meine Oberfläche nutzen kann, starte ich einen extra Thread in dem meine Berechnungen ausgeführt werden.

```
@Override
public void faceDragStart(FaceDragEvent e) {
	/* Hier passiert nicht viel, nur der Thread wird aufgerufen */
	Thread t1 = new Anim( startPos );
	t1.start();
}
```

Das eigentlich interessante geschieht natürlich in Anim:

```
@Override
public void run(){			
	double t = 0, h = 0;
	
	while ( t < 2 ){
		deformGeo.refresh();
		
		for (int index = 0; index < numOfVerts; index++){
			double[] pos = deformGeo.getCoordinates( index );
				
			/* Hier liegt das Problemchen */
			h = wave.height(pos[0], pos[1], t);
			deformGeo.translateVertex(index, h);
		}
		deformGeo.update();
				
		t += 0.01;
	}
}
```

Das Problem ist, dass die Funktion wave.height(double x, double y, double t) ziemlich aufwendig ist.
Nun kam mir die Idee vielleicht schon einiges vorweg berechnen zu lassen in einem neuerem Thread. Allerdings könnte es dann passieren, dass der zweite Thread Anim auf den der h berechnet warten müsste.

Das wahre Problem aber ist, ich kenn einiges der Theorie hinter Threads aber ich konnt sie nie wirklich anwenden.
Wie könnte ich das an dieser Stelle realisieren? Zum Beispiel das ich einen extra Thread rechnen lasse und ein anderer stets nur die Ergebnisse nutzt.
Nur.... wie geht das?  Und vor allem, würde das überhaupt nutzen tragen?

Ich hoffe ihr könnt mir helfen. Vielen Dank schon mal im voraus und liebe Grüße
Highchiller


----------



## Marco13 (23. Jan 2013)

Die Anforderungen werden nicht ganz klar, aber es klingt, als könnte man die Berechnung der Höhe mit einem ThreadPool von mehreren Threads machen lassen, während der gepostete nur zeichnet. Ob und wann er dann auf Ergebnisse warten soll oder muß ist nicht klar.


----------



## Spacerat (23. Jan 2013)

Erinnert mich nicht zuletzt wegen einer Variablenbezeichnung irgendwie an solche Berechnungen, die man anstellt, wenn man die Verformung der Oberfläche einer Flüssigkeit berechnen will, wenn da eine Masse drauf fällt (konzentrische Kreise).
Allerdings würde ich mich dann fragen, was an der Berechnung der Höhen so aufwendig sein soll. Es würde genügen, diese Wellenform pro Zeitabschnitt einmal zu Berechnen und dann die Werte mit Distanz und Richtungsvektoren auf die Oberfläche projeziert. Klappt auch wunderbar bei mehreren, sich überschneidenden Wellen, nur das man dann deren zusammentreffende Höhen addiert und durch die Anzahl der Wellen mit Amplitude != 0 teilt, aber selbst das ist nicht viel aufwendiger.


----------



## Marco13 (23. Jan 2013)

Spacerat hat gesagt.:


> Allerdings würde ich mich dann fragen, was an der Berechnung der Höhen so aufwendig sein soll.



Wenn es von einer Sache NIE genug geben wird, dann davon: Rechenleistung. (Und Speicher). Sowas wie Partikelbasierte Flüssigkeitssimulation kann beliebig aufwändig sein (notfalls modelliert man einzelne Wassermoleküle :smoke: )


----------



## Spacerat (23. Jan 2013)

Marco13 hat gesagt.:


> Wenn es von einer Sache NIE genug geben wird, dann davon: Rechenleistung. (Und Speicher). Sowas wie Partikelbasierte Flüssigkeitssimulation kann beliebig aufwändig sein (notfalls modelliert man einzelne Wassermoleküle :smoke: )


:lol: Jau... Okay... @TO: Evtl. fehlt hier noch die Info ob es ich um ein hoch wissenschaftliches Projekt oder um gängige Praxis handelt.


----------



## ww_slack (23. Jan 2013)

Wenn Du mehrere wave.height(...); Berechnungen parallelsieren möchtest kannst Du für jede einen weiteren Thread aufmachen. Das Zauberwort zum Warten lautet join():

Ob es so wirklich schneller wird hängt an der benutzen Maschine und daran, ob wave.height(...); nicht vielleicht selbst Multithreading verwendet.. in dem Fall wäre der Effekt wohl null bis negativ..



```
@Override
public void run(){          
    double t = 0, h = 0;
    
    while ( t < 2 ){
        deformGeo.refresh();

        List<Thread> meineThreads = new ArrayList<Thread>(numOfVerts);

        for (int index = 0; index < numOfVerts; index++){                           
            meineThreads.add(runSingleCalculation(index, t));
        }

        for (Thread one :meineThreads ) {
             one.join();
        }

        deformGeo.update();
                
        t += 0.01;
    }
}

private Thread runSingleCalculation(int index, double t) {

            /* Hier implementierst Du den Start eines weiteren Threads und gibst den Thread zurück:

            double[] pos = deformGeo.getCoordinates( index );
            h = wave.height(pos[0], pos[1], t);
            deformGeo.translateVertex(index, h);
            */

}
```


----------



## Highchiller (23. Jan 2013)

Ja Theorie ist das Eine, Praxis das Andere.
Es geht ja auch eher darum, dass die Berechnung aufwendig ist und ich das durch Threads optimieren möchte. Ob das jetz tatsächlich aufwendig ist oder nicht, tut eigentlich nichts zur Sache  zumindest im Moment nicht.

Vielen Dank slack.
Ich werd erst Nachts dazu kommen, dass auszuprobieren. Meld mich dann noch mal


----------



## Highchiller (24. Jan 2013)

Hmm ok das hat leider nicht funktioniert.
Ist es hier jetzt nich auch eher enorm ineffizient für jedes h nene neuen thread zu starten?

Das Ding ist halt, die Methode "update()" braucht lange, und daran kann nichts rumgeschraubt oder verändert werden. Daher dacht ich mir ein Thread könnte doch schon mal los legen. Während der eigentliche thread halt immer nach dem updaten mal nach älteren ergebnissen fragt. Sozusagen.

Könnt ihr damit was anfangen oder wär das blödsinn was ich hier rede?


----------



## Marco13 (24. Jan 2013)

Wah nein, nicht für jedes einen Thread! Am einfachsten einen Executors.fixedThreadPool mit vielleicht 4 oder 8 Threads erstellen, und dem mit executeAll die Aufgaben (d.h. die Höhenberechnungen) zuschanzen (executeAll wartet auch automatisch...)


----------



## Highchiller (24. Jan 2013)

Ah ja sowas hab ich mir schon eher vorgestellt 
Könntest du mir vielleicht ein paar codeschnipsel zu werfen wie das aussehen könnte? Wie gesagt theorie ist das eine, aber ich hab mit executers und threadpools noch nie gearbeitet.
Eventuell wären sogar Tasks angemessen, da es ja tatsächlich nur um strikte mathematische Berechnungen geht.... aber von Tasks hab ich noch viel weniger Ahnung ~.~


----------



## Spacerat (24. Jan 2013)

Also wenn "wave.height()" nicht mal das Problem ist, sondern "update()", könnte man eben genau nur diese Methode als Thread auslagern. Aber evtl. hilft's ja schon, wenn man die nach ausserhalb der "while"-Schleife legt, weil dann wird sie nur 1 statt 200 Mal aufgerufen (selbiges gilt für "refresh()").
Okay, ich weiss nicht genau was "deformGeo" ist (vermutlich 'ne Heightmap). Was passiert eigentlich bei "refresh()" und was bei "update()"? Kann es sein, dass "refresh()" zwischen einem Back- und einem Visiblebuffer hin und herschaltet und "update()" dann die Daten des eben beschriebenen Back- in den Visiblebuffer kopiert (oder etwas ähnliches)? Wenn dem so ist, wäre "update()" ohnehin an der falschen Stelle bzw. im falschen Thread.


----------



## Marco13 (24. Jan 2013)

Highchiller hat gesagt.:


> Könntest du mir vielleicht ein paar codeschnipsel zu werfen wie das aussehen könnte?



In http://www.java-forum.org/java-basics-anfaenger-themen/127689-laufzeit-verkuerzen.html#post830791 hatte ich mit der methode 'doitMT' mal ein paar Zeilen gepostet, die sehr generisch "irgendeine Berechnung" (in diesem Fall: 'doit'  ) in Teile zerlegen und parallel ausführen kann. In der 'doit' würdest du dann eben die Höhenwerte zwischen den indizes "min" und "max" berechnen (und insgesamt würden damit alle zwischen 0 und numberOfVerts berechnet)


----------



## Highchiller (24. Jan 2013)

Also das ganze funktioniert so... es basiert alles auf Java3D (nur als spezielle software entwickelt von der TUB, heißt JReality)

Im Moment habe ich einen "Torus" (im Prinzip nen Donut) der sich wie Wasser verhalten soll. Der Torus besteht aus, sagen wir 80*80 Punkten (damit die Oberfläche möglichst "smooth" ist).
In der for-schleife gehen wir nun jeden Knoten durch, berechnen seine Höhe und verschieben diesen entlang seiner Normalen. Noch wird aber nichts gezeichnet, daher geht das auch "relativ" schnell. Bis auf die Tatsache das wir jedes mal ne komplizierte Rechnung durchführen um die Höhe zu errechnen (Mathematik Studium, Schwerpunkt liegt natürlich auf Mathematik).
Update() ist dann der eigentliche Kern der Methode. Sie zeichnet/visualisiert/realisiert dann wirklich die Anzeige und zeichnet alle neuen (also verschobenen) Vertices. Würde ich jetzt update() auslagern (außerhalb der while-schleife) wäre also überhaupt nix zu sehen 
Refresh setzt einfach alles auf default zurück, damit ich nicht jedes mal neu nach oben verschoben wird sondern stets aus Höhe 0 + h gerechnet wird.

So... Also update() ist langsam und die ganze for-schleife ist natürlich auch langsam.
Theoretisch sollte update() so schnell aufgerufen werden, dass man ne flüssige Bewegung erkennt. Je mehr Vertices, desto langsamer, logisch 

@ Marco, ah ok... das schau ich mir doch mal an 
Hab Dank.

@ Spacerat, und? Fällt dir mit den Erläuterungen noch was ein? 
Auch dir, hab dank


----------



## Spacerat (24. Jan 2013)

Fast wie ich es mir gedacht habe (Okay, du has die Beschreibung der "refresh()"-Methode vergessen, aber egal).

Wie sieht's grad' aus:
Torus wird berechnet, Torus wird gezeichnet.

Wie es werden soll:
Ein Torus wird gezeichnet, während der nächste schon berechnet wird.

Daraus folgt, dass es zumindest schon mal 2 Torusse geben müsste, dessen Zugriff man in den jeweiligen Threads sperrt. Am Anfang eines Zyklus werden die Torusse ausgetauscht. Der Renderthread (der Teil in welchen die "update()"-Methode gehört) kann nun einen zeichnen während der Logikthread den anderen berechnet. "refresh()" und "update()" müssen dann auch nicht mehr so oft aufgerufen werden, sondern wie gesagt pro Renderzyklus einmal.
Wenn ich Zeit hätte, würd' ich auch 'nen KSKB erstellen.


----------



## Highchiller (24. Jan 2013)

Highchiller hat gesagt.:


> Refresh setzt einfach alles auf default zurück, damit ich nicht jedes mal neu nach oben verschoben wird sondern stets aus Höhe 0 + h gerechnet wird.


:bae:
Die refresh() Methode hab ich jetzt eh entfernt. Hab das etwas anders gelöst, tut aber nichts zur Sache.

Ja genau so hab ich mir das auch eher vorgestellt. Nur könnte es ja (wie auch immer) passieren, dass der Logikthread noch gar nicht fertig gerechnet hat und update() warten muss bis das fertig ist. Andersrum kann es aber auch passieren, dass update() zu langsam ist, dann müsste ich einfach all meine berechneten Ergebnisse speichern damit update() dann darauf zurückgreifen kann.

Nur eben genau da liegt mein Problemchen. Ich weiß nicht wie ich dem update-Thread sage, nu mach mal solangs geht, wenn nich wart halt kurz und leg dann wieder los.
Ich will gar kein ganzess KSKB. Mir würden halt theoretische Codeschnipsel schon reichen :/

Da würde es sich sicher auch anbieten gleich den Threadpool zu nutzen, wie von Marco13 beschrieben? :rtfm:


----------



## Spacerat (24. Jan 2013)

Wer lesen kann ist klar im Vorteil. Naja... die Zeile war zu klein, ist wohl keine Entschuldigung. :lol: Aber gut, dass du sie rausgenommen hast, resetten muss man so ein Objekt auf keinen Fall, wenn man beim Berechnen der Wellenform eh' eine Variable mit 0 initialisiert und dann im Torus die Werte einfach setzt.

Das Problemchen mit dem Warten, hatte ich mit "sperren" gemeint. So etwas nennt sich Monitor und einen solchen bekommt man mit dem Schlüsselwort *synchronized*. Ich bin, was Threads angeht, leider auch nicht ganz uptodate (kenne die Concurrency-API nur vom Hörensagen) und würde es deswegen mit "wait()"- und "notify()"-Konstrukten lösen. Sicher bietet diese Concurrency-API aber auch dafür etwas an.


----------



## Highchiller (24. Jan 2013)

Hmm mit wait() hät ich doch aber nichts gewonnen. Wenn der Logikthread ständig sagt, update() warte mal bis ich fertig bin, dann kann update() ja auch erst aufgerufen werden wenn wirklich alles durch exerziert wurde. Das wär ja Blödsinn. Wenn ich ihm aber nach jedem neuen berechnen sage warte bis ich mit dieser schleife durch bin, dann läuft das programm doch genau wie jetzt. Erst die for-schleife, dann update(), for-schleife, update() und so weiter...
Oder seh ich da was falsch?


----------



## Spacerat (24. Jan 2013)

Highchiller hat gesagt.:


> Hmm mit wait() hät ich doch aber nichts gewonnen. Wenn der Logikthread ständig sagt, update() warte mal bis ich fertig bin, dann kann update() ja auch erst aufgerufen werden wenn wirklich alles durch exerziert wurde. Das wär ja Blödsinn. Wenn ich ihm aber nach jedem neuen berechnen sage warte bis ich mit dieser schleife durch bin, dann läuft das programm doch genau wie jetzt. Erst die for-schleife, dann update(), for-schleife, update() und so weiter...
> Oder seh ich da was falsch?


Das siehst du falsch... gleich nach wait() werden die Buffer (Torusse) ausgetauscht und beide Threads gehen gemeinsam in den nächsten Zyklus... der eine berechnet den nächsten Schritt, der andere zeichnet den zuvor berechneten. Wie gesagt, es werden zwei Torusse benötigt. Diese hält man in einem Array, so benötigt man zum Umschalten nur noch ein "count++; count %= 2". Der Renderthread benutzt dann "Torus[count]" und der Logikthread "Torus[1 - count]". Wenn ein Thread schneller ist, muss er halt auf den anderen warten. Natürlich dauert das Berechnen und Zeichnen insgesammt eben solange wie zuvor, jedoch läuft das Ganze ja parallel ab (siehe Grafik).


----------



## Highchiller (24. Jan 2013)

Dumm gelaufen ~.~
Es wurde nicht schneller... Hier mal mein momentaner Code:

```
// Hier der Haupt-Thread (der update() aufruft)
@Override
public void run() {
	inProgress = true;
	
	double t = 0;
	double timeStep = 0.01, timeEnd = 2;
	
	int steps = (int)(timeEnd/timeStep)+1;
	saveArray = new double[steps][numOfVerts][3];
	
	synchronized (this){ // um den thread zu warten zu lassen bzw. wieder zu wecken
		
		CalculationThread compute = new CalculationThread( this, timeStep, steps );
		compute.start();
		
		try { this.wait(); } 
		catch (InterruptedException e1) {}
		
		for ( int index = 0; index < steps; index++ ){				
			if ( tmpCounter > index ){ // counter zählt bis wohin wir gerechnet haben
				deformGeo.setVertexCoordinates( saveArray[index] );
				deformGeo.update();
			} else { // falls wir noch nicht soweit sind, index zurücksetzen und warten
				try { 
					index--; 
					this.wait();
				} catch (InterruptedException e) { System.out.println("Wait failed"); }	
			}
		}
		
	}
	
	inProgress = false;
}

// Hier die Klasse für die Berechnungen der Höhe aller Knoten
private class CalculationThread extends Thread{
	private AnimationThread masterThread;
	private double timeSteps;
	private int steps;
	
	private CalculationThread( AnimationThread masterThread, double timeStep, int steps ){
		this.masterThread = masterThread;
		this.timeSteps = timeStep;
		this.steps = steps;
		tmpCounter = 0;
	}
	
	@Override
	public void run(){
		for( int step = 0; step < steps; step++ ){
			double t = step*timeSteps;
			saveArray[step] = deformGeo.defaultVertices.clone();
			
			for (int index = 0; index < numOfVerts; index++){
				double[] pos = deformGeo.getCoordinates( index );
				double h = wave.height(pos[0], pos[1], t);
				saveArray[step][index] = deformGeo.translateVertexTEST(index, h);
			}
			
			synchronized ( masterThread ){
				tmpCounter++;
				masterThread.notify(); // update-thread wieder aufwecken
			}
		}
		
	}
	
}
```

Schade, da brauch die Berechnung der Höhe aller Vertices wohl doch deutlich länger als update().
Jetzt würde sich halt wieder anbieten die Höhe doch via Threadpool zu berechnen oder was meint ihr?
Noch Ideen? 
Wie gesagt, Tasks sollen angeblich deutlich fixer und effizienter sein nur... da hab ich echt mal gar kein Plan von :/


----------



## andiv (24. Jan 2013)

Ich hab mir jetzt deinen Code nicht angeschaut. Ich kann dir aber das Buch "Java Concurrency in Practice" und darin insbesondere die Abschnitte über "Producer-Consumer-Probleme" empfehlen. Das hat mir beim Verständnis von Threads und Executors u.s.w. sehr geholfen.

Hast du eigentlich schonmal mit einem Profiler (z.B. JVisualVM) gemessen, an welchen Stellen der Flaschenhals liegt?


----------



## Highchiller (24. Jan 2013)

Zeile 58, wave.getHeight ist das Problem 
Da es um eine approximative Näherung an eine Wellengleichung im 2 Dimensionalen geht, dauert das erstaunlich lang.
Ich schätze ich nutz dafür nen Pool wie von Marco13 oben empfohlen. Die Hauptarbeit dafür hab ich ja bereits getan. Und mehr ist dann wohl wirklich nur noch im mathematischen Hintergrund zu holen 

Habt dank


----------



## Marco13 (24. Jan 2013)

Beim Draufschauen sehe ich da jetzt erstmal nur EINEN Thread, hab's aber nicht gelesen...

EDIT: Oh, das war wohl noch VOR dem letzten Beitrag...


----------



## Highchiller (25. Jan 2013)

Grönau 

Ich werd mich mal in Tasks einlesen und versuchen damit was zu stande zu kriegen. Das Ergebnis kann ich ja hier noch mal posten.


----------



## Highchiller (25. Jan 2013)

Sooo... Also die Laufzeit lässt immer noch zu wünschen übrig aber mehr ist nicht drin. Wenn man sich vorstellt das für jeden Punkt (bei ungefähr 100*100 Knoten) die Höhe approximativ durch 2 ineinander von 1 bis 15 laufende for-schleifen berechnet wird, ist das schon sehr akzeptabel 

Hier der Code (gekürzt)

```
@Override
public void run() { // die haupt-run methode
	/* blabla vorweg, variablen definieren und co */
	synchronized (this){
		CalculationThread compute = new CalculationThread( this, timeStep, steps );
		compute.start(); // startet einen thread für die berechnung aller translationen zu jedem zeitpunkt
		
		for ( int index = 0; index < steps; index++ ){			
			if ( tmpCounter > index ){ // wenn die berechnung schon durchgeführt wurde, updaten
				deformGeo.update( saveArray[index] ); // saveArray enthält translatierte vectoren
				
				try { this.sleep(40); } // falls die calculations zu schnell werden
				catch (InterruptedException e) { e.printStackTrace(); }
			} else { // falls man noch nicht so weit ist, erst mal warten, dann den index noch mal prüfen
				try { 
					index--;
					this.wait();
				} catch (InterruptedException e){}	
			} // if-else
		} // for
	} // synchronized
	
	deformGeo.refresh(); // auf ausgansposition zurücksetzen
	inProgress = false;
}

private class CalculationThread extends Thread{
	/* menge blabla, konstruktor und co */

	@Override
	public void run(){
		ForkJoinPool transPool = new ForkJoinPool(); // erstellt task-pool
		
		for( int step = 0; step < steps; step++ ){ // für jeden zeitschritt den task-pool rechnen lassen
			double t = step*timeSteps;
			transPool.invoke( new TranslateTask( saveArray[step], 0, numOfVerts-1, t ) );
			
			synchronized ( masterThread ){
				tmpCounter++; 
				masterThread.notify(); // update() aufwecken
			}
		}
		
	}	
} // CalculationThread

private class TranslateTask extends RecursiveAction{
	/*auch wieder blabla vorweg */

	@Override
	protected void compute() {			
		if ( end - start < 100 ){ // direkt berechnen wenns weniger als 100 knoten sind
			for (int index = start; index <= end; index++){
				double[] pos = deformGeo.getCoordinates( index );
				double h = wave.height(pos[0], pos[1], time);
				verts[index] = deformGeo.translateVertex(index, h);
			}
			return;
		} /* wenn nicht dann teilen wir die aufgaben auf mehrere tasks auf */

		int middle = (start+end)/2;
		TranslateTask transFirst = new TranslateTask( verts, start, middle, time );
		transFirst.fork();
		
		TranslateTask transSecond = new TranslateTask( verts, middle+1, end, time );
		transSecond.compute();
		transFirst.join();
	}
}
```

Eine Frage an dem Punkt vielleicht noch. Ich habs aus der neuen version von "Java, Mehr als eine Insel".
Da stand halt auf dem ersten task rufen wir ".fork()" auf.
Auf dem zweiten dann ".compute()" (das is klar)
und DANACH noch mal ".join()" auf den ersten. Das letzte ist mir unklar. Bzw. ist mir fork() nicht ganz klar.
Was würde passieren würde ich .fork() weglassen? Bzw. was wenn ich .join() weglassen würde?

Vielen Dank und eine gute Nacht wünsch ich 
Highchiller


----------



## Spacerat (25. Jan 2013)

fork - englisch bedeutet Gabel. Damit ist jetzt nicht die Gabel, mit der man isst (konsumiert) gemeint, sondern eher eine Weggabelung. Programmtechnisch teilt sich dort der Programmfluss in 2 parallel laufende Zweige. Ich frag' mich nur, was an dieser Stelle ein "join()" zu suchen hat, weil bei einem solchen ja auf einen Thread gewartet wird, bis er beendet wurde. Der langsamere Thread soll sich aber nicht beenden, sondern er soll von vorne beginnen und den schnelleren darüber informieren, dass es weiter gehen kann. Da müsste ich nun auch die fragen, die sich mit Concurrency besser auskennen: Werden bei ForkJoin etwa laufend neue Threads erzeugt?
Ich hätte das ganz pragmatisch so gelöst:

```
public class RenderAndCalc {
	private Torus[] buffers = new Torus[] { new Torus(), new Torus() };
	private Thread renderer, calculator;
	private int count;

	RenderAndCalc() {
		renderer = new Renderer();
		calculator = new Calculator();
	}

	void start() {
		renderer.start();
		calculator.start();
	}

	public static void main(String[] args) {
		RenderAndCalc rnc = new RenderAndCalc();
		rnc.start();
	}

	class Renderer extends Thread {
		@Override
		public void run() {
			synchronized (this) {
				try {
					wait();
				} catch (InterruptedException e) {
					interrupt();
				}
			}
			synchronized (buffers[count]) {
				// wenn der torus gerade noch berechnet wird, geht's hier nicht
				// weiter
				buffers[count].update();
			}
		}
	}

	class Calculator extends Thread {
		@Override
		public void run() {
			synchronized (buffers[1 - count]) {
				// wenn der torus gerade noch gezeichnet wird, geht's hier nicht
				// weiter
				buffers[1 - count].calc();
			}
			count++;
			count %= 2;
			synchronized (renderer) {
				renderer.notify();
			}
		}
	}
}

class Torus {
	private Wave wave;
	private int numVerts;

	void update() {
		// TODO Auto-generated method stub
	}

	void calc() {
		double t = 0, h = 0;
		while (t < 2) {
			for (int index = 0; index < numVerts; index++) {
				double[] pos = getCoordinates(index);
				h = wave.height(pos[0], pos[1], t);
				translateVertex(index, h);
			}
			t += 0.01;
		}
	}

	/**
	 * @param index  
	 * @param h 
	 */
	private void translateVertex(int index, double h) {
		// TODO Auto-generated method stub
	}

	/**
	 * @param index  
	 */
	private double[] getCoordinates(int index) {
		// TODO Auto-generated method stub
		return null;
	}
}

class Wave {
	/**
	 * @param d 
	 * @param e  
	 * @param t 
	 */
	public double height(double d, double e, double t) {
		// TODO Auto-generated method stub
		return 0;
	}
}
```
Die erstellten Klassen Torus und Wave sind natürlich Dummies und können durch die existierenden ausgetauscht werden, bis auf eine Einschränkung: die Methode "<Torus>.calc()" müsste entweder so beim Original erstellt werden oder ihr Inhalt (inkl. numVerts und wave) im Thread dorthin kopiert werden, wo sie aufgerufen wird und die Referenz zum Torus gesetzt werden.


----------



## Marco13 (25. Jan 2013)

Hmja, das ForkJoin hat mich auch irritiert. Es ist ja von Anfang an bekannt, was gemacht werden muss, und das Problem kann sehr leicht in gleichgroße Teile zerlegt werden (die auch gleich viel Zeit benötigen). Einen Anlass, das durch ForkJoin zu machen, sehe ich nicht, aber ob es mit einem "pragmatischeren" Ansatz schneller oder langsamer wäre, ist schwer zu sagen...


----------



## Highchiller (25. Jan 2013)

Also die Geometry zu kopieren wär nicht so praktisch. Das wird aber nicht ersichtlich wenn man das ganze Programm nicht kennt.
Aber das mit dem synchronized von spacerat hat mich vorran gebracht. Ich wusste nämlich nicht wie man das verwendet. Jetzt ist mirs klar 

Zum neuen Ansatz mit ForkJoin. Er ist schon massiv schneller. Ich nutz jetz auch 100% der CPU und erreich damit schon einiges. Mit Threads würd ich das vermutlich auch schaffen aber es wurde immer wieder erwähnt das eine derartige Aufgabenzerteilung mit ForkJoin einfach effizienter wäre. Zumal die Verteilung auf Threads und nebenläufigkeit vom Pool anscheinend selbst gesteuert wird.

Und zu guter letzt zu dem join() am ende.
Ich habs mal weggenommen und er hat etliche Vertices nicht verschoben. Ich tippe darauf, dass ForkJoin den letzten join() befehl benötigt um die beiden laufenden Tasks quasi zu verknüpfen. Sie sollen ja parallel arbeiten also so effizient wie möglich gleichzeitig alles berechnen.
Ohne join() würde task1 aber task2 gar nicht kennen.
Das ist allerdings nur ne Vermutung, richtig drinne steck ich in der Materie bei weitem nicht 

Wie dem auch sei, es funktioniert. Vielen Dank
Hab ma wieder ne Menge gelernt.


----------



## Spacerat (25. Jan 2013)

Ui... das Kopieren der GeoDaten ist aber essentiell, für die Funktion meiner Version, denn sie dienen als Monitore für die jeweiligen Threads. Kopiert werden sie ja auch nicht, es werden blos zwei gleiche Geometrien erstellt und immer wieder verwendet, bei anderen Methoden dürfte es nicht anders sein. Der Hintergrund ist einfach der, dass keiner der beiden Threads auf die Geometrie des jeweils anderen zugreifen darf, sonst gibt's Kaudawelsch.
Nehmen wir mal an, beide Threads würden die selbe Geometrie verwenden und der Calculator wäre definitiv langsamer. Der Renderer würde dann mit den Daten der Laufenden Runde korrekt anfangen, früher oder später auf alte bzw. anfangs sogar ungültige Daten stossen, weil der Calculator halt noch nicht soweit ist. Das wäre dann Runde für Runde das Gleiche. Darüber hinaus würden auch beide Threads wieder gegeneinander (bzw. hintereinander) laufen, weil eben nur ein Objekt als Monitor dient. BTW.: Ein Monitor kann nur zu einem Thread gehören. Ist der Monitor eines Objekts bereits durch einen anderen Thread belegt, muss der, der drauf Zugreifen will solange warten, bis der andere ihn wieder freigegeben hat.


----------



## Highchiller (25. Jan 2013)

Naja alles was ich momentan mache ist die Koordinaten der Vertices zu kopieren und diese dann zu updaten. Damit ich nicht ausversehen daten verwende die noch gar nicht berechnet wurden hab ich ja einen counter (ziemlich simple idee) eingebaut der mir immer den index des neusten berechneten ergebnisses zurück gibt. damit fang ich ab was ich nutzen darf und was nicht.

Wenn mein updater schon zugreifen will, obwohl der counter noch nicht so weit ist, wird gewartet bis meine calculate ihn wieder weckt. (was nach jedem neuen counter++ geschieht)
Ziemlich simple eigentlich. Speichertechnisch sicherlich verbesserungswürdig aber es funktioniert


----------



## Spacerat (25. Jan 2013)

Highchiller hat gesagt.:


> Damit ich nicht ausversehen daten verwende die noch gar nicht berechnet wurden...


Genau dazu ist der 2. Torus da (bedenke, dass sie in ihren Threads gesperrt werden).
Das ist zwar nicht besonders gut für den Speicher, aber um so besser für die Performance und der korrekten Anzeige. Das Ganze wäre so eine Art Doublebuffering. Bei Morphobjekten würde ich mich zu keiner Zeit gegen dieses Verfahren wehren, es sei denn du hast bereits 'nen Shader dafür, der das alles auf der Graka berechnet, aber ich denke mal, dass man auch da 2 Buffer verwenden würde.
Die Daten eines bereits gezeichneten Torus dürften doch eigentlich nicht mehr von belangen sein. Falls doch, dann muss halt ein 3. her, der im Calculator-Thread die aktuellen Berechnungen speichert.


----------



## Highchiller (26. Jan 2013)

Ja stimmt schon, die Idee des doubleBuffers.
Allerdings liegt halt die Geometry in einer SceneGraphComponent. Ne extra SceneGraphComponent erstellen und dann die sichtbarkeit ändern? Da zweifel ich doch. Und die Geometry einer SceneGraphComponent ändern dauert.
Passt jetz schon so


----------



## Sym (26. Jan 2013)

Ich würde da ein anderes Modell zu Grunde legen. Wenn es wenig Punkte sind, kann man mit finiten Elementen arbeiten. Oder einfach ein Federmodell. Es ist klar, dass es langsam ist, wenn Du jeden Punkt einzeln berechnest.


----------

