FPS geht mit Kollisionsberechnung in die Knie

  • Antworten:10
DennisG
  • Forum-Beiträge: 35

18.12.2012, 21:05:59 via Website

Halli Hallo,
ich hab eine einfache Kollisionsberechnung mit 2 for-schleifen und einer x und einer y Array erstellt und abgefragt ob Koordinaten x+1 y, x-1 y, x y-1, x y+1, also alle 4 Richtungen mit einem Pixel belegt sind. Nun bei 500 wdh. bei 30 FPS geht es noch ohne probleme, aber ab 1.000 wdh. anfängt zu stottern und ab 2.000 wdh. es fast komplett stoppt, kann man da noch mehr raus holen?

Also Kollisionsberechnungen mit 40.000 und mehr Objekten für 30 FPS sind möglich wie es bei http://dan-ball.jp/en/javagame/dust/ der Fall ist, jetzt zwar nur auf dem PC aber es müsste noch eine ähnliche rate bzw. 5.000 erreicht werden können oder?
Nur wie? Wo lässt sich die Power aus einem Smartphone raus holen?

Liebe Grüße,
Dennis :D

— geändert am 18.12.2012, 21:06:36

Antworten
Gelöschter Account
  • Forum-Beiträge: 281

18.12.2012, 22:52:51 via Website

Was lässt du kollidieren, um jedes Pixel prüfen zu müssen?

Antworten
DennisG
  • Forum-Beiträge: 35

19.12.2012, 00:26:18 via Website

also hier mein Objekte nannte ich Pixel, diese sind einfach nur 1x1 große Rechtecke und diese Rechtecke müssen ja geprüft werden ob sie miteinander Kollidieren, also ob sie nach Rechts Links Oben oder Unten sich bewegen können.

— geändert am 19.12.2012, 00:28:05

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

19.12.2012, 11:35:02 via Website

Zeige doch mal den Code deiner 2 Schleifen.
Ev. läßt sich da noch was auf unterer Ebene optimieren.

Ansonsten muß man was intelligenteres auf hörer Ebene machen (Boxing)

Antworten
DennisG
  • Forum-Beiträge: 35

19.12.2012, 20:51:38 via Website

Der Code:
[code]
// DURCHLAUF
boolean darftouchen = true;
for(int i=0;i<pixel_type.length;i++)
{


// ERLAUBUNGEN
boolean runterfallen = true;
boolean rechts = true;
boolean links = true;

// KOLLISIONS BERRECHNUNGEN
for(int q=0;q<pixel_type.length;q++)
{

if(q != i)
{
// ABSTAND
int absx = Math.abs(pixel_x[q] - pixel_x[i]);
int absy = Math.abs(pixel_y[q] - pixel_y[i]);

// RECHTS
if(pixel_x[i] < pixel_x[q] && absx < pixsize*2 && absy < pixsize*2)
{
rechts = false;
}

// LINKS
if(pixel_x[i] > pixel_x[q] && absx < pixsize*2 && absy < pixsize*2)
{
links = false;
}

// PRFÜFEN VERBIETEN
if(pixel_y[q] > pixel_y[i] && pixel_y[q]-pixsize*2 < pixel_y[i] && absx < pixsize*2)
{
// PIXEL DRUNTER IM WEG
runterfallen = false;

if(pixel_type[i] == 0 && pixel_type[q] == 1 || pixel_type[i] == 1 && pixel_type[q] == 0)
{
pixel_type[q] = 2;
pixel_type[i] = 2;
/*pixel_x[i] = 0;
pixel_y[i] = 0;*/
}
if(pixel_type[i] == 1 && pixel_type[q] == 2 || pixel_type[i] == 2 && pixel_type[q] == 1)
{
pixel_type[q] = 3;
pixel_type[i] = 3;
/*pixel_x[i] = 0;
pixel_y[i] = 0;*/
}
q = pixel_type.length-1;
}
}
}

if(pixel_type[i] != 0){
// AUSFÜHREN WAS ERLAUBT
if(runterfallen && pixel_type[i] != 0)
{
if(pixel_y[i] < vh)
{
// DAMIT IM BILD BLEIBT
if(pixel_type[i] == 0) {
pixel_y[i] += 1;
}
if(pixel_type[i] == 1) {
pixel_y[i] += 2;
}
if(pixel_type[i] == 2) {
pixel_y[i]+=5;
}
if(pixel_type[i] == 3) {
pixel_y[i]+=3;
}
}
}
else
{
if(rechts && links)
{
int zz = (int) Math.round(Math.random());
if(zz == 0)
{
//RECHTS
pixel_x[i]+= Math.random()*pixsize;
}
if(zz == 1)
{
//LINKS
pixel_x[i]-= Math.random()*pixsize;
}
}
else
{
if(rechts)
{
pixel_x[i]+= Math.random()*pixsize;
}
if(links)
{
pixel_x[i]-= Math.random()*pixsize;
}
}
}
}

}
[/code]

— geändert am 19.12.2012, 20:51:58

Antworten
Gelöschter Account
  • Forum-Beiträge: 281

20.12.2012, 08:58:52 via Website

Also nach der Berechnung der Abstände könntest du zum Beispiel prüfen, ob der Abstand der Pixel ingesamt so gross ist, dass sie gar nichts miteinander zu tun haben können und dann aus dem Schleifendurchlauf rausspringen. Dann könntest du vielleicht mit Vorzeichen rechnen, das würde dir die ganzen Vergleiche in der Art von [code]if (pixel_x[i]<pixel_x[q][/code] sparen.

Als nächstes wären da noch solche Stellen:

[code]
if(pixel_type[i] == 0) {
pixel_y[i] += 1;
}
if(pixel_type[i] == 1) {
pixel_y[i] += 2;
}
if(pixel_type[i] == 2) {
pixel_y[i]+=5;
}
if(pixel_type[i] == 3) {
pixel_y[i]+=3;
[/code]

Warum hast du keine Datenstruktur, in der die Inkremente, die du hier angibst, stehen? Dann könntest du z.B. schreiben

[code]
pixel_y[i]+=pixel_down_inc[pixel_type];
[/code]

Das wäre dann nur eine Zeile, ohne Vergleiche.

Generell gilt: Auf Hardware-Ebene gesehen können if-Abfragen ziemlich teuer werden - Stichworte wären da Execution Pipelines, Instruction Caching und Branch Prediction...

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

20.12.2012, 10:00:55 via Website

Hi Dennis !

Codeoptimierung finde ich eine sehr spannenden Aufgabe, sie kann den Unterschied zwischen Nicht-Durchführbarkeit des Projektes und Erfolg ausmachen.
Meine Kenntnissse beruhen auf C/C++ Optimierung. Ich denke, aber dass bei Java es ähnlich läuft.

Dein Code ist nicht optimal, ich würde ihn sogar das extrem schlecht bezeichnen - so daß Du noch enormes Potential hast. Pi mal Daumen sind 99.9 deiner Berechnungen während der Laufzeit überflüssig wenn mans umformt.

Jede noch so kleine Rechenoperation kostet Zeit.
So auch
pixsize*2 (wenn pixsize keine Konstante ist)
pixel_X[i] ist eine Operator - die Adresse der Variable zu berechnet werden (&X[0]) + i*sizeof(int) in C

Diese und ähnliche Operationen werden permanent in der inneren Schleife ausgerechnet.

Du solltes alles das was in der inneren Schleife konstant ist nur einmal außen berechnen und in der inneren Schleife verwenden.

int PixsizeMal2 = pixsize * 2
int Pixel_Xi = Pixel_x[i]
etc.

switch verwenden statt die cases mit vielen ifs testen.

Die Logik umbauen:

if(pixel_x[i] < pixel_x[q] && absx < pixsize*2 && absy < pixsize*2)
rechts = false;
if(pixel_x[i] > pixel_x[q] && absx < pixsize*2 && absy < pixsize*2)
links = false;

Diese beiden Ifs enthalten teilweise dieselbe Bedingunge.
Wie java die ifs mit mehreren Bedingungen optimiert weiß ich nicht. In C++ kann man einstellen, das nicht alle Bedingungen getestet werden, wenn die 1. schon nicht stimmt.
Jedenfall würde ich umformulieren

if(absx < pixsize*2 && absy < pixsize*2)
{
if(pixel_x[i] < pixel_x[q])
...........
if(pixel_x[i] > pixel_x[q])
................
}
Also generell so schachteln, so das möglichst früh und möglichst einfach die "großen" Entscheidungen getroffen werden.

if(a && b && c)
if(a && b && d)
===>
if(a && b)
if(c)
if(d)

So allg. betrachtet lassen sich deine 2 Schleifen bestimmt triangulieren und einen factor 2 sparen
D.h.
Kommt für indicees q i bzw. i q nicht dasselbe raus.
int absx = Math.abs(pixel_x[q] - pixel_x[i]);
-------------------------------------------------------------------------------------
Versuche mal die Optimierungen einzubauen.
Da ich auch an Java-Code-Optimierung interessiert bin wäre es nett wenn Du berichtest was das gebracht hat, damit wir auch was lernen.
Deinen neuen Code kannst Du ja dann posten. Ich finden bestimmt noch was zum optimieren.

— geändert am 20.12.2012, 10:24:06

Antworten
Gelöschter Account
  • Forum-Beiträge: 281

20.12.2012, 10:21:13 via Website


Diese beiden Ifs enthalten teilweise dieselbe Bedingunge.
Wie java die ifs mit mehreren Bedingungen optimiert weiß ich nicht. I C++ kann man einstellen, das nicht alle Bedingungen getestet werden, wenn die 1. schon nicht stimmt.

Short-Curcuit Evaluation funktioniert natürlich auch in Java, d.h. nach der ersten zutreffenden Bedingung wird abgebrochen.

@Dennis: Deine for-Schleifen selbst solltest du auf vorab berechnete Variablen umstellen.

Also statt

1for(int q=0;q<pixel_type.length;q++)

solltest du schreiben:

1int array_len = pixel_type.length;
2for(int q=0;q<array_len;q++)

Ansonsten wird wohl auch bei jedem Schleifendurchlauf pixel_type.length ausgewertet.

Ansonsten bin ich wie Andreas auf deine Ergebnisse gespannt ;-)

— geändert am 20.12.2012, 10:22:37

Antworten
DennisG
  • Forum-Beiträge: 35

20.12.2012, 15:52:37 via Website

Ja also schon mal vielen Dank, konnte von einer FPS rate von 4 bei 1000 Pixeln auf 15 steigern, sieht recht flüssig aus :)
Bei 1500 Pixeln sinkt die Rate leider auf 6 und bei 2000 gehts runter auf 2 FPS.
Ich habe wichtige if Abfragen nach oben verschoben und unwichtiges rausgehauen, zudem alle Variablen im vor hinein berechnet.
Nun aber die Frage, war es das schon? Lässt sich sonnst nichts mehr raus holen?

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

20.12.2012, 17:47:57 via Website

Ohne Code kann ich nicht viel dazu sagen!
Vermute aber stark dass Du nicht alles optimiert hast.

Diese kleinen Tips waren erstmal eine Übung um zu lernen wie man auf unterer Ebene schnellen Code entwicklet.

Ich denke den großen Performanceschub bekommst Du erst, wenn Du den Algorithmus grundsätzlich umbaust (andere Logik)
Hab das Ziel im Moment nicht ganz vor Augen - im Moment auch keine Zeit das zu analysieren und daher noch keine Idee.

Die Ordnung in müßte eigenlich nicht größer als O(n^2) sein. Daher wundert es mich, das du bei Erhöhnung der Problemgröße um eine Faktor 2 (1000->2000) 8 mal langsamer wirst. Da stimmt was nicht!
(Beruht deine Geschw. messung nur auf dem gezeigten Code oder ist die Grafikausgabe oder was anderes da mit drin?)

Spontane Idee:
Du könntes die Pixel jeweils nach x und y sortieren O(2 * n*log(n)) - also sehr schnell
Damit könntest Du für die jeweiligen Koordinatenrichtungen die Nachbarschaftssuche extrem beschleunigen. Mußt natürlich eine Indexebene mehr verwalten. Denke aber das es sehr viel bringt. Wie gesagt das Problem ist O(n^2)!
Wenn du die Ordnung drückst und das auch nur für Teile des Codes hast Du gewonnen.

Poste erstmal Deine jetzigen Code - bevor wir die große Lösung angehen.

— geändert am 20.12.2012, 17:48:31

Antworten
Gelöschter Account
  • Forum-Beiträge: 281

20.12.2012, 18:00:00 via Website

Genau, her damit ;-)

Antworten