Gravitation - Aufbau und Optimierung

chmee

verstaubtes inventar
Premium-User
Hellau :) Sitze mal wieder an den typischen spiel-mit-der-Physik-Sachen rum. Gravitation und viele Massepunkte sind nett anzuschauen, umso mehr, desto besser.

In meiner Signatur ist LBM2, ein kleiner Benchmark mit jenem Thema. Ich möchte aber die Anzahl der Partikel drastisch erhöhen, mehrere 10er Potenzen. Der Benchmark ist in Blitzmax geschrieben, das tut aber nix zur Sache, es geht um jegliche Steigerung der Geschwindigkeit.

bisherige Ideen
  • Gleitkomma durch Int ersetzen ohne Verlust der Genauigkeit
  • Schleife optimieren
  • Grundformel simplifizieren bei gleichem Ergebnis.
  • Inline Assembler für schnelle Berechnungen ?
  • Engine(Dx oder OGL) benutzen oder direkt in den Speicher schreiben
Deklaration:
Code:
' -------------- Auflösung
Global SX:Int=1024
Global SY:Int=768 
Global Xmid:Int=SX/2
Global YMid:Int=SY/2
' ---------------- Anzahl Punkte, Multiplikator für die Kräfte und Skalierung bei Darstellung
Global Points:Int=500
Global G:Float=0.1
Global Scaling:Float=0.2
' --------------------------- Arrays für die Punkte
Global xx:Float[Points]
Global yy:Float[Points]
Global Ex:Float[Points]
Global Ey:Float[Points]
Global Fo:Float[Points]
Global Fx:Float[Points]
Global Fy:Float[Points]
' -------------------- Variablen für die Berechnung
Global Winkel:Float
Global Abstand:Float
Global Xf:Float
Global Yf:Float
Global Rad:Float
Global Radx:Float
Global Rady:Float
Global Kra:Float
Global KrP:Float

Punkte initialisieren ( Punkt 0 ist gesondert initialisiert, ich gebe ihm eine besondere Masse, einer Sonne vergleichbar )
Code:
xx[0]=0
yy[0]=0
Fo[0]=200
Fx[0]=0
Fy[0]=0
' --------------- Startwerte der Punkte per Zufall - Besonderheit - per Winkel und Abstand gesetzt
For a=1 To Points-1
 Winkel=Rnd(0,360)
 Abstand=Rnd(200,380)
 xx[a]=Sin(Winkel)*Abstand
 yy[a]=Cos(Winkel)*Abstand
 Fo[a]=Rnd(1,8)
 Fx[a]=-yy[a]*0.3
 Fy[a]=xx[a]*0.3
Next
Die Hauptschleife sieht folgendermaßen aus,
sie sollte wohl für alle Coder sofort klar sein:
Code:
While Not(KeyHit(KEY_SPACE))
 Cls
 For p=0 To Points-1
' -----------------------Initialwert Bewegungsvektor
  Xf=Fx[p]
  Yf=Fy[p]
  For q=0 To Points-1
   If Not(q=p) Then
' ------------------------- horiz und vert. Entfernung
    Radx=xx[q]-xx[p]
    Rady=yy[q]-yy[p]
' ---------------------------- Pythagoras ohne Wurzel - Entfernung
    Rad=Radx*Radx+Rady*Rady
' ----------------------------- Skalarwert der Kraft abh. von Entfernung
    Kra=G*Fo[q]*Fo[p]/Rad
    KrP=Fo[q]/Fo[p]
' ------------------------------ Addiere zum Bewegungsvektor
    Xf:+(Kra*Radx*KrP)
    Yf:+(Kra*Rady*KrP)
   EndIf
  Next
' ------------------------------ Speichere die neue Kraft im Array
  Fx[p]=Xf
  Fy[p]=Yf
' ------------------------- Verschiebe den Punkt um den Kräftevektor
  xx[p]:+Fx[p]
  yy[p]:+Fy[p]
' ------------------------- Umrechnen der Weltkoordinaten in ein Bildschirmwert  
  Plot XMid+xx[p]*Scaling,Ymid+yy[p]*Scaling
' ---------------------------- Wenn Punkt 0 ( Sonne ), male ihn Besonders
  If p=0 Then DrawOval XMid+xx[p]*Scaling-3,Ymid+yy[p]*Scaling-3,7,7
 Next
' -------------------------- Doublebuffer Flip
 Flip
Wend
Der Code ist aus Blitzmax.
Alle Ideen sind gerne gehört :D Wie gesagt, ich möchte mindestens in den Bereich 20.000 Points/Frame bei mind. 25Fps. Bei erfolgreicher Zusammenarbeit würde ich daraus gerne ein Tutorial zusammenstellen, verschiedene Sprachen, Optimierungswege, Umsetzung der physikalischen Formeln etc.. Natürlich mit Nennung aller Teilnehmer an diesem Thread.

mfg chmee

p.s.: Man könnte sich auch als Ziel setzen, gemeinsam einen tutorials.de-Bildschirmschoner zu bauen, wie sagt man so schön, eye candy soll er sein. Interessant wären auch vollständige GPU-Umsetzungen in HLSL mit Motionblur etc..
 
Mir siehts so aus als koenntest du Fx und Fy sparen, die Kraefte koenntest du sofort zum Verschieben verwenden. Wegen multicores waer Parallelisierung mit openMP eine Idee. Ausserdem koenntest du noch die Approximation einfuehren, dass weit entfernte Partikel keinen Einfluss haben und einen Cut-off einfuehren.
 
Danke erstmal für einige Ansätze. Ja, man könnte FX und Fy einsparen, spart lediglich 2 Additionen in der Schleife ( was aber bei n*(n-1), also knapp n^2 Durchläufen schon so Einiges bringen könnte ). Das mit der Ausgliederung weit entfernter Punkte hab ich schon getestet, ich finde, man sieht der Sache an, dass nicht mehr alle Punkte einbezogen werden.

Ich glaube ( habe das Gefühl ), dass ich viel tun kann, wenn ich die Floatwerte durch Integer ersetze ( einfach mehrere Faktoren höher ) und bei der Ausgabe die Division durch jenen Faktor durchführe. Weiterhin bin ich an InlineAssembler interessiert, um die Berechnung quasi direkt in Assembler durchzuführen. Ich habe das Programm auch mal in C übersetzt, leide da aber an meiner Unwissenheit bezüglich Optimierung und Hardwaregraphics.

Ich habe auch schon überlegt, ob ich alle Punkte NUR von einem Massepunkt abhängig mache, quasi also eine Universum-Simulation, wo dann die Schleife auf n Durchläufe minimiert wird. Ist aber im Aussehen nicht das Selbe.

mfg chmee
 
Ich hab deinen Code eben nur überflogen, aber ich glaube ich hab mal sowas ähnliches gemacht. In C mit SDL. Ich häng das einfach mal an. Es läuft mit > 500 Objekten auf jedenfall noch flüssig...Hab jetzt aber leider nichtmehr getestet...
(Man kann keine .tar.gz hochladen, irgendwer hat versagt!)
 

Anhänge

  • stars.zip
    117 KB · Aufrufe: 17
Interessant, dass Du C+SDL ansprichst, hab ich auch schon benutzt, während ich aber unter kompiliertem Blitzmax etwa 1400 Sterne hatte, waren es unter C+SDL nur etwa 700 bei 25fps.

Ich schau mir später Dein Source mal an.

mfg chmee
 
Ich glaube ( habe das Gefühl ), dass ich viel tun kann, wenn ich die Floatwerte durch Integer ersetze ( einfach mehrere Faktoren höher ) und bei der Ausgabe die Division durch jenen Faktor durchführe. Weiterhin bin ich an InlineAssembler interessiert, um die Berechnung quasi direkt in Assembler durchzuführen.
Bevor man zu solchen „drastischen“ Methoden greift, sollte man erst mal versuchen, „algorithmisch“ zu optimieren, d.h.: die Komplexität runterschrauben, sofern möglich. Teile von Hand in Assembler zu implementieren bringt nur in recht speziellen Fällen Vorteile. Moderne C-Compiler verfügen inzwischen über sehr ausgefeilte Optimierungstechniken, sodass man da per Hand meistens nur was rausholen kann, wenn man sich sehr gut mit dem Befehlssatz des jeweiligen Prozessors auskennt (wenn überhaupt).

Zu den möglichen Optimierungen sollte das Stichwort „N-body simulation“ gute Suchergebnisse liefern.

Grüße, Matthias
 
Ne einfache möglichkeit ist auch einfach die Welt in ein gitter aufzuteilen und zu jeder Zelle Verknüfungen auf die vorkommenden Partikel spiechern. Dann muss man für einen Partikel nur die in den nachbarzellen berechnen, da alle anderen, die weiter weg sind, vermutlich eh kaum einfluss auf diesen Partikel haben.
 
@OnlyFoo :

Hmm, auf Anhieb würde ich sagen Nein, das bringt nicht viel. 1. ist Gravitation auch bei recht hohen Entfernungen noch maßgeblich am Ergebnis beteiligt und 2. hat man pro Durchgang ( sagen wir mal Fullscreen 1024x768 ) 1024*768*4~3,2Mio. Berechnungen durchzuführen bei sehr viel geringerer Genauigkeit ( siehe 1. ) im Vergleich zu zB 7000*6999~49Mio Berechnungen ( Ok, das ist der Faktor 10, aber eben 1. )

Erinnert mich auch mehr an Fluids-Dynamics, siehe Hier und Hier

Danke trotzdem für die Gedanken und Ideen. mfg chmee
 
@OnlyFoo :
nd 2. hat man pro Durchgang ( sagen wir mal Fullscreen 1024x768 ) 1024*768*4~3,2Mio. Berechnungen durchzuführen bei sehr viel geringerer Genauigkeit ( siehe 1. ) im Vergleich zu zB 7000*6999~49Mio Berechnungen ( Ok, das ist der Faktor 10, aber eben 1. )


Ich möchte nicht den Bildschirm für jeden Subpixel in ein gitter aufteilen, sondern vllt in 100x100 Felder. Und gehe dann natürlich auch nur durch die Felder, in denen Punkte sind. Ob das groß was ausmacht ist denke ich abängig von der Simulationsgröße, aber stimmt natürlich....
 

Neue Beiträge

Zurück