Gleich vorweg - das hier ist kein Problem oder ähnliches, was einer dringenden Lösung betrifft, darum stehts auch im OT. Ich hab nur ne seltsame (aber auch winzigkleine) Beobachtung gemacht, als ich gerade meinen Code optimiert hab, die ich mir als nicht-Informatiker nicht erklären kann. Vllt. klärt mir jemand das Miraculum auf
Ich hab mir nen kleinen Raytracer geschrieben, und bin wie oben beschrieben also gerade dabei meinen Code zu optimieren. Die Zeitmessungen mach ich alle mehrmals und mittele dann, um zufällige Schwankungen auszuschließen. Meine Test-Szene renderte vor meiner "Optimierung" in ~5800ms. Dann hab ich mich an folgende Codestelle gemacht:
Code:
for i := 0 to Scene.SceneCount - 1 do begin if not Scene[i].InheritsFrom(TVisibleObject) then Continue; Hit := Scene[i].Intersect(a_Ray, Distance); if Hit <> 0 then Primitive := Scene[i]; end;
Durch Änderungen am Scene-Objekt hab ich sichergestellt, dass die default-Eigenschaft nur TVisibleObject-Nachfolger enthält und dachte mir dann "Gut, jezze brauchste die dritte Zeile wohl nimmer, wo du das prüfst - nimmste sie raus, is immerhin eine if-Abfrage weniger pro Strahl", gesagt, getan: Auskommentiert, Zeitmessung, uuuund - der Rendervorgang dauert länger. Und zwar sagenhafte 30-40ms länger (wie gesagt, es sind schon gemittelte Werte). Ja, ich weiß, die Änderung is winzig, aber sie ist da und ich kann sie mir partout nich erklären - ich spare der CPU doch Arbeit, wieso also dauert die Sache plötzlich länger?
Nochmal: Ich weiß es is nich weltbewegend, ich finds auch nich dringend und weils wahrscheinlich nix ist, was die Welt (außer mich) interessiert stehts ja auch im OT ^^
Edit: Ich sammel hier einfach mal weiter, worüber ich noch so stoße... Warum ist der berühmte FastInvSQRT in Delphi:
Code:
Function FastInvSQRT(v: Single): Single; Var XHalf: Single; I: Integer Absolute v; X2: Single Absolute I; Begin XHalf:= 0.5 * v; I:= $5f3759df - (I SHR 1); Result := X2 * (1.5 - XHalf * X2 * X2); end;
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Kann ich mir spontan nicht erklären, nur mit einer (ungeprüften!) Hypothese. InheritsFrom könnte eventuell nur testen, ob Vorfahrenklassen TVisibleObject sind, nicht aber ob das Objekt selbst eine Instanz von TVisibleObject ist. Damit würden eventuell (ich kenne deine Szene nicht) einige Objekte, die direkt von TVisibleObject erben, rausfallen. Aber wie gesagt, das ist nur eine Hypothese und ich kenne die genaue Funktion von InheritsFrom nicht. Der "is" Operator prüft auf jeden Fall auch ob die aktuelle Klasse gleich ist, das aber nur am Rande.
Der "is"-Test oder auch InheritsFrom sind auf jeden fall etwas komplexere Tests (will sagen, mehr als nur ein Integer-Vergleich z.B.), also ists schon erstaunlich, wenn du durch die einsparung kosten erzeugst.
Hast du eventuell im Getter, der hinter Scene[i] steht, irgendwas dazu gebaut?
grüße (ps: Eine andere Optimierung wäre übrigens, Scene[i] zwischenzuspeichern. Propertyzugriffe sind immer etwas teurer als normale Variablenzugriffe, vorallem, wenn da hinter den Kulissen noch auf eine Liste zugegriffen wird, wo Range Checks etc. vorkommen)
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Zum FastInvSQRT: Rechnest du außerhalb eventuell mit Double oder Extended (oder auch dem Metatyp "float" der entweder Double oder Extended ist)? Dann muss danach eine Umwandlung Double<>Extended gemacht werden, was Zeit kostet. Außerdem haben heutige CPUs bei Floating-Point-Operationen schon mächtig Zahn drauf, irgendwer hat hier im Forum auch mal geschrieben, dass die sogar eher auf Float als auf Integer optimiert werden, was eventuell erklärt, warum die Int-Lösung langsamer ist.
// Edit: Nach Allgemein verschoben. Darf ruhig gefunden werden und passt hier trotz mangelnder Dringlichkeit besser rein.
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Vorab: Ich könnt mich köstlich amüsieren über das, was meine Zeiten so sagen...
Nacheinander:
Lord Horazont hat geschrieben:
Kann ich mir spontan nicht erklären, nur mit einer (ungeprüften!) Hypothese. InheritsFrom könnte eventuell nur testen, ob Vorfahrenklassen TVisibleObject sind, nicht aber ob das Objekt selbst eine Instanz von TVisibleObject ist. Damit würden eventuell (ich kenne deine Szene nicht) einige Objekte, die direkt von TVisibleObject erben, rausfallen. Aber wie gesagt, das ist nur eine Hypothese und ich kenne die genaue Funktion von InheritsFrom nicht. Der "is" Operator prüft auf jeden Fall auch ob die aktuelle Klasse gleich ist, das aber nur am Rande.
Der "is"-Test oder auch InheritsFrom sind auf jeden fall etwas komplexere Tests (will sagen, mehr als nur ein Integer-Vergleich z.B.), also ists schon erstaunlich, wenn du durch die einsparung kosten erzeugst.
Hast du eventuell im Getter, der hinter Scene[i] steht, irgendwas dazu gebaut?
TVisibleObject ist nur eine abstrakte Klasse, die die Funktionen Intersect und GetNormal (als abstract) einführt - die gibts in der Szene nicht. Hab auch mal statt dem "Continue" ein "Beep" reingeschrieben, um zu testen, ob da vielleicht doch mal gesprungen wird - nein, es wird nicht gesprungen. Aber mit dem "Is"-Operator hast du mich auf ne Idee gebracht - ich hab das .InheritsFrom mal durch ein "is" ersetzt - bringt tatsächlich nen paar ms. Der Getter für Scene[i] sieht wie folgt aus:
Code:
function TScene.GetScene(Index: Integer): TVisibleObject; begin Result := nil; if Index > High(FScene) then Exit; Result := FScene[Index]; end;
Is also auch eher komisch, dass ich Zeit spare, wenn ich ihn aufrufe...aber dazu später
Lord Horazont hat geschrieben:
grüße (ps: Eine andere Optimierung wäre übrigens, Scene[i] zwischenzuspeichern. Propertyzugriffe sind immer etwas teurer als normale Variablenzugriffe, vorallem, wenn da hinter den Kulissen noch auf eine Liste zugegriffen wird, wo Range Checks etc. vorkommen)
Die Idee fand ich gut, also hab ich mich auf die Suche nach Codestellen gemacht, wo mir das was bringen könnte (das Scene[i] von oben landet nach dem Intersection-Test sofort in ner anderen Variable, da brachte es also sicher nichts) und bin bei meinen Lichtern fündig geworden - die stehen in Scene.Lights[Index: Integer], also auch in ner Liste, der Getter sieht exakt aus wie der oben angegebene - nur halt für TLight statt für TVisibleObject und ich guck natürlich auch in nem andern Array. Also hab ich der Raytracingprozedur eine neue Variable verpasst (Light: TLight), und das aktuelle Licht (Scene.Lights[j]) brav zwischengespeichert. Noch fix alle "Scene.Lights[j]" durch "Light" ersetzt, Raytracer angeworfen, zurückgelehnt, Ergebnis gesehen, aufgelacht: Langsamer. Das hat mich dann dazu bewogen, den Kompilierungsmodus von "Debug" mal auf "Release" zu stellen - wollts nur erwähnen, geändert hats nichts. Dabei sollte das doch eigentlich nen Geschwindigkeitszuwachs bringen, meine ich mal gehört zu haben...
Edit: Nachtrag zum Getter Nachdem im Code alle Zugriffe auf Scene[Index] oder Scene.Lights[Index] nur aus Schleifen heraus erfolgen, die eh nur bis Count - 1 laufen hab ich die Tests ob der Index höher ist als der höchste Feldindex sinnvollerweise mal aus den Gettern entfernt - Ergebnis: ca. 100ms schneller.
Lord Horazont hat geschrieben:
Zum FastInvSQRT: Rechnest du außerhalb eventuell mit Double oder Extended (oder auch dem Metatyp "float" der entweder Double oder Extended ist)? Dann muss danach eine Umwandlung Double<>Extended gemacht werden, was Zeit kostet. Außerdem haben heutige CPUs bei Floating-Point-Operationen schon mächtig Zahn drauf, irgendwer hat hier im Forum auch mal geschrieben, dass die sogar eher auf Float als auf Integer optimiert werden, was eventuell erklärt, warum die Int-Lösung langsamer ist.
Alle meine Fließkommazahlen sind Singles (und machen ordentlich einen drauf, Schenkelklopfer). Aber das heutige CPUs einfach anders optimiert sind, klingt natürlich plausibel.
Neuere nützliche Beobachtungen, die auch einen Sinn ergeben (a: Single): - a * a ist deutlich schneller als Sqr(a) - auch wenn das Internet teilweise etwas anderes sagt ist a + a nicht schneller als 2 * a - Um mehrere Floats durch einen Wert a zu teilen ist Floats * 1 / a schneller als Floats / a, wenn man 1 / a vorberechnet (Beispielsweise beim Vektoren normieren)
Bezüglich des zwischenspeichern von Scene[i] bzw. TLight. Du darfst dabei natürlich das TLight nicht kopieren. In C++ nutze ich für sowas gerne eine konstante Referenz:
Soviel ich weiß werden, verbergen sich bei Delphi hinter Variablen, die scheinbar Instanzen von Klassen enthalten doch sowieso immer nur Pointer auf die Instanz.
Zumindest ist es so, dass wenn ich...
Code:
type TFoo = class Bar: Integer end;
//Somewhere later... var a, b: TFoo; begin a := TFoo.Create; a.Bar := 1; b := a; b.Bar := 5; Print(IntToStr(a.Bar)); end;
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
zoiX hat geschrieben:
- a * a ist deutlich schneller als Sqr(a) - auch wenn das Internet teilweise etwas anderes sagt ist a + a nicht schneller als 2 * a - Um mehrere Floats durch einen Wert a zu teilen ist Floats * 1 / a schneller als Floats / a, wenn man 1 / a vorberechnet (Beispielsweise beim Vektoren normieren)
Oh mein Gott. Ich muss das unbedingt mal überprüfen. Ich warte gerade eh auf ne Systeminstallation, ich glaube, ich mache mal einen Arithmetik-Benchmark für FreePascal.
greetings
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Soviel ich weiß werden, verbergen sich bei Delphi hinter Variablen, die scheinbar Instanzen von Klassen enthalten doch sowieso immer nur Pointer auf die Instanz.
Achso...ok...in C++ ist das anders.
Zitat:
a * a ist deutlich schneller als Sqr(a)
Vermutlich weil Sqr ein Funktionsaufruf ist und nicht inline abgehandelt wird.
Zitat:
auch wenn das Internet teilweise etwas anderes sagt ist a + a nicht schneller als 2 * a
Eine Addition ist schneller als eine Multiplikation. Allerdings ist 2*a ein Spezialfall den du durch einen einfachen Bit-Shift lösen kannst. => Vermutlich optimiert da der Compiler.
Eine Addition ist schneller als eine Multiplikation. Allerdings ist 2*a ein Spezialfall den du durch einen einfachen Bit-Shift lösen kannst. => Vermutlich optimiert da der Compiler.
Tut er eher nich, denn BitShifts bei Floats sind doch...nich so gut, oder? Ich glaub ich geh jezze mal einkaufen und komm dann mal im IRC vorbei,wenn der Thread gerade eh hochfrequentiert is xD
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Ich hab den Benchmark mal umgesetzt und ausgeführt. Jeder Test wird 10000000 mal ausgeführt. Im Anhang die Ausgabe des Programms einmal mit -O1 (normale, debuggerfreundliche Optimierung) und einmal mit -O3 (sehr starke Optimierung).
Getestet auf einem 64-Bit System. Die Werte sind relativ zu betrachten, also ist die Geschwindigkeit der CPU irrelevant.
Für die faulen gibts auch noch mal die Tests kurz ausgeschrieben, soweit nicht selbsterklärt:
Code:
SquareDirect: A * A SquareFunc: Sqr(A) SquarePower: Power(A, 2) DoubleAdd: A + A DoubleMult: 2 * A
(Nachtrag: hab gerade shl 1 für verdopplung von Ints ausprobiert, zumindest der FPC macht das schon automatisch (dauert genauso lange))
grüße
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Bis auf den letzten Punkt liegen meine Feststellungen also entweder am Compiler (Codegear RAD Studio hier), oder sind "subjektiv" - wie auch immer das passiert ist. Aber das Divisionen teilweise so extrem viel langsamer sind als Multiplikationen hatte ich offen gestanden nicht erwartet...
Kannst du vielleicht noch gerade benchmarken wieviel schneller ein Test auf > 0 gegen über > 0.x ist? Ich hab nämlich bei meinem Specular-Licht mal vor die Berechnung der Lichtintensität (in der ja ein Power(Single, 20 (oder irgendwas anderes großes als Exponent halt)) vorkommt, abgefragt, ob das zur zwanzigsten Potenz erhobene Skalarprodukt überhaupt groß genug ist, um nach der Rechenoperation noch wahrnehmbar zu sein. Hatte mir dadurch offen gestanden einiges an Gewinn erhofft, aber dem war nicht so (der Code war schon schneller, aber nich allzu signifikant). Vorher stand da nur die Frage, ob das Skalarprodukt größer als 0 sei.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Jup, neue Ergebnisse angehangen (Source auf anfrage).
Nop tut exakt garnichts, ist einfach eine leere Methode um den Overhead durch einen Methodenaufruf herauszubekommen. Was mir dabei auffällt ist, dass Zero ( A = 0) teilweise weniger braucht als das, was darauf hinweisen könnte, dass der Test wegoptimiert wird. Ist möglicherweise aber auch einfach Abweichung.
greetings
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: So Sep 26, 2010 12:54 Beiträge: 238 Wohnort: wieder in Berlin
Programmiersprache: Englisch
das FastInvSqrt ist deswegen so "lahm" weil es "damals" zu zeiten von lahmen rechnern schnell war........ die recheneinheiten von heute, können mittlerweile viel viel viel schneller wurzeln ziehen und quadrate bilden... würde mich auch nicht wundern wenns nicht auch bei den multimedia erweiterungen solche "funktionen" fürs invertiertewurzeln gibt.
berüchtigt ist es auch nur weil carmack genaue werte nicht interessierten und ihm einfach ein näherungswert reichte - und eben diese lassen sich per pi mal daumen schneller errechnen.
Naja, Artikel / Blogeinträge berichten auch von mehr als 600% Geschwindigkeitsgewinn mit dem FastInvSqrt-Algorithmus gegenüber 1 / Sqrt(x), so mies war das also nich...und die Quake 3-Engine hat ja auch schon recht lang Anwendung gefunden
Mitglieder in diesem Forum: 0 Mitglieder und 3 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.