Discussion:
FastMath
(zu alt für eine Antwort)
Manfred Polak
2018-08-28 23:16:12 UTC
Permalink
Moin!

Ich schreibe gerade mit 10.2 Community Edition ein Programm für
Ljapunow-Diagramme. Funktioniert auch so weit, ist aber nicht besonders
schnell. Da muss eine einfache Formel, die einen Logarithmus enthält,
sehr oft iteriert werden, und ich nehme an, dass der Logarithmus der
Hauptbremser ist.

Meine Versuche, das etwas zu beschleunigen, sind alle gescheitert.
Zuerst hatte ich die Idee, Double durch Single zu ersetzen, aber dadurch
ist das Programm nicht schneller, sondern langsamer geworden. Von
Assembler und Intrinsics habe ich keine Ahnung, fällt also flach, wenn
ich da keine genaue Anleitung bekomme. Dann habe ich die Unit Math
durch DAMath von Wolfgang Ehrhardt ersetzt. Hat im Prinzip funktioniert,
aber auch dadurch wurde das Programm langsamer statt schneller.

Und schließlich bin ich auf FastMath gestoßen. Das soll laut Eigen-
beschreibung deutlich schneller sein als die Standardarithmetik von
Delphi, und hat außer dem ohnehin schon schnelleren Logarithmus
noch eine extraschnelle Variante FastLog2. Nur blöd, dass ich das
nicht zum Laufen bekomme. Das Paket ist ins Projekt eingebunden,
die Unit Neslib.FastMath steht in der uses-Klausel und wird dort auch
erkannt. Nur wird dann keine der Funktionen von FastMath von Delphi
akzeptiert. Schreibe ich nur den Funktionsnamen, also "Log2", heißt
es "undeklarierter Bezeichner 'Log2' in Zeile ...", schreibe ich
"Neslib.FastMath.Log2", heißt es "Neslib.FastMath enthält kein Element
namens 'Log2'". Dabei bietet FastMath das in seiner Dropdown-Liste
sehr wohl an. Und genauso verhält es sich auch mit allen anderen
Funktionen von FastMath. Sie werden angeboten, aber wenn ich sie
verwenden will, sind sie nicht bekannt. Bei anderen Leuten scheint
FastMath ja zu funktionieren. Was mache ich da falsch?


Manfred
Hans-Peter Diettrich
2018-08-29 08:31:57 UTC
Permalink
Post by Manfred Polak
namens 'Log2'". Dabei bietet FastMath das in seiner Dropdown-Liste
sehr wohl an. Und genauso verhält es sich auch mit allen anderen
Funktionen von FastMath. Sie werden angeboten, aber wenn ich sie
verwenden will, sind sie nicht bekannt. Bei anderen Leuten scheint
FastMath ja zu funktionieren. Was mache ich da falsch?
Vielleicht klemmt es an irgendeinem $IF?

DoDi
Manfred Polak
2018-08-30 16:03:33 UTC
Permalink
Post by Hans-Peter Diettrich
Post by Manfred Polak
namens 'Log2'". Dabei bietet FastMath das in seiner Dropdown-Liste
sehr wohl an. Und genauso verhält es sich auch mit allen anderen
Funktionen von FastMath. Sie werden angeboten, aber wenn ich sie
verwenden will, sind sie nicht bekannt. Bei anderen Leuten scheint
FastMath ja zu funktionieren. Was mache ich da falsch?
Vielleicht klemmt es an irgendeinem $IF?
Wie kann ich denn sowas herausfinden?


Manfred
Hans-Peter Diettrich
2018-08-30 16:07:53 UTC
Permalink
Post by Manfred Polak
Post by Hans-Peter Diettrich
Post by Manfred Polak
namens 'Log2'". Dabei bietet FastMath das in seiner Dropdown-Liste
sehr wohl an. Und genauso verhält es sich auch mit allen anderen
Funktionen von FastMath. Sie werden angeboten, aber wenn ich sie
verwenden will, sind sie nicht bekannt. Bei anderen Leuten scheint
FastMath ja zu funktionieren. Was mache ich da falsch?
Vielleicht klemmt es an irgendeinem $IF?
Wie kann ich denn sowas herausfinden?
Irgendwelche fehlerhafte Zeilen in die Bibliothek einbauen, über die der
Compiler stolpern muß. Tut er das nicht, kommt er an dieser Stelle
garnicht vorbei. Dann die Zeile nach oben verschieben, bis der Compiler
meckert.

DoDi
Manfred Polak
2018-08-30 20:08:34 UTC
Permalink
Post by Hans-Peter Diettrich
Post by Manfred Polak
Post by Hans-Peter Diettrich
Vielleicht klemmt es an irgendeinem $IF?
Wie kann ich denn sowas herausfinden?
Irgendwelche fehlerhafte Zeilen in die Bibliothek einbauen, über die der
Compiler stolpern muß. Tut er das nicht, kommt er an dieser Stelle
garnicht vorbei. Dann die Zeile nach oben verschieben, bis der Compiler
meckert.
Das hat nichts ergeben. Aber der Fehler tritt ja auch schon vor dem
Kompilieren in Erscheinung, schon im Editor. Ich bin ratlos.


Manfred
Hans-Peter Diettrich
2018-08-31 06:18:56 UTC
Permalink
Post by Manfred Polak
Post by Hans-Peter Diettrich
Post by Manfred Polak
Post by Hans-Peter Diettrich
Vielleicht klemmt es an irgendeinem $IF?
Wie kann ich denn sowas herausfinden?
Irgendwelche fehlerhafte Zeilen in die Bibliothek einbauen, über die der
Compiler stolpern muß. Tut er das nicht, kommt er an dieser Stelle
garnicht vorbei. Dann die Zeile nach oben verschieben, bis der Compiler
meckert.
Das hat nichts ergeben.
Was hattest Du denn erwartet?
Post by Manfred Polak
Aber der Fehler tritt ja auch schon vor dem
Kompilieren in Erscheinung, schon im Editor. Ich bin ratlos.
Dann laß erst mal alles weg, was diese Fehler verursacht, und teste nochmal.

Kann es sein, daß die Bibliothek bzw. Unit falsch abgelegt wurde und
garnicht gefunden wird?

Gibts kein Beispielprogramm dazu?

DoDi
Manfred Polak
2018-08-31 23:01:47 UTC
Permalink
Post by Hans-Peter Diettrich
Post by Manfred Polak
Post by Hans-Peter Diettrich
Irgendwelche fehlerhafte Zeilen in die Bibliothek einbauen, über die der
Compiler stolpern muß. Tut er das nicht, kommt er an dieser Stelle
garnicht vorbei. Dann die Zeile nach oben verschieben, bis der Compiler
meckert.
Das hat nichts ergeben.
Was hattest Du denn erwartet?
Da ich, wie gesagt, ziemlich ratlos bin, wusste ich nicht, was ich
erwarten soll. Ich hab's halt einfach mal gemacht.
Post by Hans-Peter Diettrich
Post by Manfred Polak
Aber der Fehler tritt ja auch schon vor dem
Kompilieren in Erscheinung, schon im Editor. Ich bin ratlos.
Dann laß erst mal alles weg, was diese Fehler verursacht, und teste nochmal.
Kann es sein, daß die Bibliothek bzw. Unit falsch abgelegt wurde und
garnicht gefunden wird?
Glaube ich nicht. Erstens würde dann "Neslib.FastMath" in der uses-
Klausel angemeckert werden, und das ist nicht der Fall. Zweitens habe
ich alles so gemacht wie sonst auch in solchen Fällen. Die Bibliothek
ist unter \Gemeinsame Dateien\BDS\ untergebracht und wurde über das
Menü "Projekt - Dem Projekt hinzufügen" eingebunden. Alles genauso
wie bei DAMath, das ja funktioniert hat, auch wenn es keinen Gewinn
an Performance brachte.
Post by Hans-Peter Diettrich
Gibts kein Beispielprogramm dazu?
Gerne. Der Effekt tritt nicht nur bei meinem eigentlichen Programm auf,
sondern auch bei einem frisch angelegten Minimalprogramm:

| unit Unit1;
|
| interface
|
| uses
| Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
| Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Neslib.FastMath;
|
| type
| TForm1 = class(TForm)
| Button1: TButton;
| Label1: TLabel;
| procedure Button1Click(Sender: TObject);
| private
| { Private-Deklarationen }
| public
| { Public-Deklarationen }
| end;
|
| var
| Form1: TForm1;
|
| implementation
|
| {$R *.dfm}
|
| procedure TForm1.Button1Click(Sender: TObject);
| begin
| Label1.Caption := FloatToStr( log2(10) );
| end;
|
| end.

Da ist dann im Editor das "log2" rot unterringelt, und in dem Fenster
links oben steht die schon erwähnte Meldung "Fehler: Undeklarierter
Bezeichner 'Log2' in Zeile ...". Dabei ist log2 eben schon eine der
Funktionen in Neslib.FastMath. Und der Effekt tritt nicht nur bei
dieser Funktion auf, sondern vermutlich bei allen. Jedenfalls bei
ungefähr 15, die ich ausprobiert habe.

Was auch immer dahintersteckt, es ist nicht lebenswichtig, und ich
habe jetzt erst mal keine Zeit mehr dafür. Also von mir aus können
wir das Thema ruhen lassen, außer es hat noch jemand eine
zündende Idee.


Manfred
Hans-Peter Diettrich
2018-09-01 03:57:08 UTC
Permalink
Post by Manfred Polak
Post by Hans-Peter Diettrich
Post by Manfred Polak
Aber der Fehler tritt ja auch schon vor dem
Kompilieren in Erscheinung, schon im Editor. Ich bin ratlos.
Und wenn Du kompilierst?
Post by Manfred Polak
Post by Hans-Peter Diettrich
Gibts kein Beispielprogramm dazu?
Gerne. Der Effekt tritt nicht nur bei meinem eigentlichen Programm auf,
Ich meinte eines, das mitgeliefert wurde.
Post by Manfred Polak
Da ist dann im Editor das "log2" rot unterringelt
Ich weiß nicht, wieviel sich da inzwischen geändert hat, aber zu meiner
Zeit war das ein Grund, dieses Fietscher abzuschalten.

DoDi
Manfred Polak
2018-09-03 22:27:22 UTC
Permalink
Hans-Peter Diettrich schrieb:

So, jetzt konnte ich mich noch mal damit befassen und den Fehler
finden.
Post by Hans-Peter Diettrich
Und wenn Du kompilierst?
Dann wurde das Kompilieren mit einer Fehlermeldung (inkompatible
Typen) abgebrochen, was aber nur ein Folgefehler war, weil die fragliche
Funktion (die mit dem Logarithmus) nicht richtig kompiliert wurde.
Der Fehler steckte aber gar nicht im log2, sondern in den banalen
Funktionen abs und trunc, die ich jetzt als System.abs bzw. System.trunc
schreiben muss, damit es funktioniert. Jetzt kompiliert es, und das
Programm funktioniert auch so, wie es soll.
Post by Hans-Peter Diettrich
Post by Manfred Polak
Da ist dann im Editor das "log2" rot unterringelt
Ich weiß nicht, wieviel sich da inzwischen geändert hat, aber zu meiner
Zeit war das ein Grund, dieses Fietscher abzuschalten.
Scheint so. Zumindest in diesem Fall ist die Fehlermeldung definitiv
irreführend. Jedenfalls danke für deine Antworten!


Manfred
Manfred Polak
2018-09-06 23:30:35 UTC
Permalink
Post by Manfred Polak
Und schließlich bin ich auf FastMath gestoßen. Das soll laut Eigen-
beschreibung deutlich schneller sein als die Standardarithmetik von
Delphi, und hat außer dem ohnehin schon schnelleren Logarithmus
noch eine extraschnelle Variante FastLog2.
Ich wollte nur noch kurz anmerken, dass FastMath tatsächlich ziemlich
genial ist. Schon das Ersetzen des Standard-Log2 durch FastLog2 hat
mein Programm um ein Drittel schneller gemacht, ohne Qualitätsverlust
bei den Bildern. Aber der Knüller sind die Vektor-Operationen von
FastMath, die die SSE2-Einheiten der CPUs benutzen. Indem ich jetzt
jeweils vier Pixel auf einmal berechnen lasse, wurde das Programm
nochmal um einen Fakter von mehr als 4 schneller. Woher der Betrag
kommt, der über 4 hinausgeht, weiß ich nicht, aber er ist da. Insgesamt
wurde das Programm so um einen Faktor von ungefähr 5,5 schneller.
Da kann man nicht meckern, und die Mühe hat sich gelohnt.


Manfred
M. Behrendt
2018-11-24 12:16:48 UTC
Permalink
Post by Manfred Polak
Meine Versuche, das etwas zu beschleunigen, sind alle gescheitert.
Zuerst hatte ich die Idee, Double durch Single zu ersetzen, aber dadurch
ist das Programm nicht schneller, sondern langsamer geworden. Von
Assembler und Intrinsics habe ich keine Ahnung, fällt also flach, wenn
ich da keine genaue Anleitung bekomme. Dann habe ich die Unit Math
durch DAMath von Wolfgang Ehrhardt ersetzt. Hat im Prinzip funktioniert,
aber auch dadurch wurde das Programm langsamer statt schneller.
auch wenn es schon etwas älter ist:
Je nach Prozessortyp und compiler ist der native Datentyp real schneller als double, single ist jedoch in fast allen Kombinationen Prozessor/Compiler noch schneller.

Schleifen immer per "repeat .. until" und nicht mit "for .. to" aufbauen und
im repeat until mit inc()/dec() arbeiten, ist wiederum schneller als i:=i+1;

Die Workload auf threads aufteilen hast du ja schon erledigt.

Per VCL.Graphics auf die canvas pinseln lassen ist ultralangsam, ich hab es durch OpenGL ersetzt.
Alfred Gemsa
2018-11-24 17:28:12 UTC
Permalink
Post by M. Behrendt
im repeat until mit inc()/dec() arbeiten, ist wiederum schneller als i:=i+1;
Das dürfte nicht stimmen:

Das CPU-Debug-Fenster zeigt zum einen:

Unit1.pas.44: k := k + 1;
0045AEC5 46 inc esi

zum anderen:

Unit1.pas.33: inc(k);
0045AE55 46 inc esi

Compiler-Optimierung war eingeschaltet.

Gruß, Alfred.
M. Behrendt
2019-05-14 16:27:38 UTC
Permalink
Post by Alfred Gemsa
Post by M. Behrendt
im repeat until mit inc()/dec() arbeiten, ist wiederum schneller als i:=i+1;
Unit1.pas.44: k := k + 1;
0045AEC5 46 inc esi
Unit1.pas.33: inc(k);
0045AE55 46 inc esi
Compiler-Optimierung war eingeschaltet.
Gruß, Alfred.
Ja, das ist korrekt Alfred.
Ich war jetzt in Gedanken bei der Theorie.
Ich hatte benchmarks der verschiedenen Datentypen ohne Compileroptimierung durchgeführt (Turbo Delphi 6.0), um solche "Überarbeitungen" seitens des Compilers zu vermeiden.
Bei der Programmierung der Schleifen fiel mir o.g. auf.
Dürfte aber, wie du schreibst nicht praxisrelevant sein, da kaum jemand ohne Compileroptimierung arbeitet. (Schätze ich).
Manfred Polak
2018-11-27 23:06:08 UTC
Permalink
Post by M. Behrendt
Post by Manfred Polak
Meine Versuche, das etwas zu beschleunigen, sind alle gescheitert.
Zuerst hatte ich die Idee, Double durch Single zu ersetzen, aber dadurch
ist das Programm nicht schneller, sondern langsamer geworden. Von
Assembler und Intrinsics habe ich keine Ahnung, fällt also flach, wenn
ich da keine genaue Anleitung bekomme. Dann habe ich die Unit Math
durch DAMath von Wolfgang Ehrhardt ersetzt. Hat im Prinzip funktioniert,
aber auch dadurch wurde das Programm langsamer statt schneller.
Je nach Prozessortyp und compiler ist der native Datentyp real schneller als double, single ist jedoch in fast allen Kombinationen Prozessor/Compiler noch schneller.
Da ich ja FastMath doch noch zum Laufen gebracht habe, hat sich diese
Frage erübrigt, weil FastMath in der derzeitigen Version sowieso nur
Single unterstützt (was ich etwas schade finde - ich hoffe, dass es auch
mal eine Version mit Double gibt).
Post by M. Behrendt
Schleifen immer per "repeat .. until" und nicht mit "for .. to" aufbauen und
im repeat until mit inc()/dec() arbeiten, ist wiederum schneller als i:=i+1;
Ich nehme an, dass das nur dann ins Gewicht fällt (falls überhaupt,
siehe Antwort von Alfred Gemsa), wenn pro Schleifendurchlauf nur eine
relativ einfache Operation durchgeführt wird. Bei mir ist jedenfalls
innerhalb der zentralen Funktion der Logarithmus der zeitkritische
Faktor. Durch Ersetzen der Standardversion von Delphi durch die
Version von FastMath wurde das Programm schon mal deutlich
schneller.

Und bei den beiden äußeren Schleifen, die das Bild zeilen- und spalten-
weise aufbauen, spielt die Art der Schleife von der Geschwindigkeit her
sowieso keine Rolle. Wobei ich für die Spalten while ... do verwende,
weil da die Schrittweite 4 statt 1 ist (es werden immer 4 Pixel auf
einmal berechnet).
Post by M. Behrendt
Die Workload auf threads aufteilen hast du ja schon erledigt.
Ja, das hat gut geklappt, und es skaliert wunderbar mit meinen vier
CPU-Kernen. Vermutlich auch mit mehr Kernen, konnte ich aber noch
nicht testen.
Post by M. Behrendt
Per VCL.Graphics auf die canvas pinseln lassen ist ultralangsam, ich hab es durch OpenGL ersetzt.
Im Prinzip sicher richtig, aber bei mir bedeutungslos, weil hier die
weitaus meiste CPU-Arbeit in den Iterationen steckt. Das sind pro
Pixel mindestens einige hundert Iterationen, für ein gutes Bild aber
eher einige tausend bis über zehntausend. Den gewonnenen Wert
dann in eine Farbe zu übersetzen und diese pixelweise ins Bild zu
schreiben, ist vom zusätzlichen Zeitbedarf her vernachlässigbar.
Wobei ich anfangs tatsächlich die Canvas von TBitmap benutzt hatte,
aber später auf Graphics32 umgestiegen bin. Es wird aber immer
noch pixelweise das Bild aufgebaut.


Manfred
Loading...