Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Plasmadarstellung mit Farbrotation


Mathematiker - Mo 10.09.12 21:58
Titel: Plasmadarstellung mit Farbrotation
Hallo,
obwohl einige Forumnutzer schon von meinen "kontextlosen Mathematikfragen" (Zitat!) genervt sind, gibt es noch ein Thema mehr, zum Ärgern! :P
Mit richtiger Mathematik hat es aber weniger zu tun.

Ich habe versucht den Diamond-Square-Algorithmus (http://de.wikipedia.org/wiki/Diamond-square_Algorithmus) nur mit Delphi, ohne Open GL, umzusetzen, um ein Bild zu erzeugen, dass einem Plasma ähnelt.
Nach dem Start des beiliegenden Programms kann man eine Farbpalette auswählen, das Bild zeichnen und anschließend die Farben "rotieren" lassen, wodurch, meiner Meinung nach, der Plasmaeindruck noch deutlicher hervortritt.

Entsprechend des Algorithmus wird die Darstellung rekursiv erzeugt. Und hier ist mein Problem.
Die Konstruktion des Plasmas dauert einige Zeit, da ich im Moment keine Möglichkeit sehe, mit Scanline oder Ähnlichem den Algorithmus zu beschleunigen. Würde man die Pixel zeilenweise generieren, so wäre Scanline optimal. Die Farben der Pixel werden aber durch die Rekursion als Randpunkte von Rechtecken erzeugt.
Vielleicht sieht jemand von Euch eine Möglichkeit, die Bilderzeugung zu beschleunigen. Danke.

Beste Grüße
Mathematiker

Rev 3: Deutlich schnelleres Zeichnen des Bildes (ohne Scanline, durch Einführung eines Feldes).
Rev 4: Etwas schneller und (hoffentlich) einen Fehler beseitigt.
Rev 5: Weitere verschiedene Änderungen.
Rev 6: Etwas ASM, aber vor allem hoher Geschwindigkeitsgewinn durch Austausch von canvas.pixels. Jetzt ist es richtig schnell!
Rev 7: Ohne Assembler-Code, der wahrscheinlich fehlerhaft ist.
Rev 8: Code etwas optimiert. Die Größe des Programmfensters ist veränderbar. Der Assembler-Text ist enthalten, wird aber noch nicht aufgerufen.
Rev 9: Erhebliche Verringerung der Berechnungsanzahl.
Rev 10: Die Berechnungszahl ist gleich der Punktzahl, zumindest für etwa quadratische Zeichenflächen.
Rev 11: Korrigierte Version.
Rev 12: Das Programm enthält wahlweise die Fensterroutine von Horst_H und meine. Berechnungen können gezählt werden. Bild wird bei Größenänderung automatisch neu erstellt.
Rev 13: Die Farbpaletten sind in die Exe als Ressource integriert.
Rev 14: verschiedene Änderungen, u.a. dfm-Datei im binary-Format
Rev 15: Sauberer Bildaufbau auch bei Windows XP und 2000, außerdem weitere Beschleunigung (rund 10%) durch Entfernung von real-Variablen.
Rev 16: weitere Beschleunigung: Bildaufbau in weniger als 50 ms (auf meinem PC).


Delphi-Laie - Mo 10.09.12 22:42

So ganz verstehe ich das Problem nicht.

Wenn man über die Combobox eine Farbpalette auswählt, ist nach kurzer Verzögerung das Startbild präsent. Man kann es sich über "Darstellung" noch einmal (oder ein ähnliches?) erzeugen lassen (das ist in der Tat ziemlich langsam), doch wozu? Die Farbrotation als eigentliches Demonstrationsobjekt dieses Programmes wird davon nicht berührt.

Die Rekursion scheint nicht bis zur Größe eines Pixels hinab zu arbeiten (neudeutsch: herunterzubrechen). Benutzt Du dazu den Rectangle-Befehl? Der ist m.E. schon ziemlich schnell.


Mathematiker - Mo 10.09.12 22:54

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Man kann es sich über "Darstellung" noch einmal (oder ein ähnliches?) erzeugen lassen (das ist in der Tat ziemlich langsam), doch wozu? Die Farbrotation als eigentliches Demonstrationsobjekt dieses Programmes wird davon nicht berührt.

Genau die Erzeugung des Bildes ist mir zu langsam. Dass jedes Mal über den Schalter Darstellung ein neues Bild gezeichnet wird, wollte ich eigentlich auch haben. Mitunter weichen die Bilder doch erheblich ab, vor allem wenn der Faktor geändert wird.
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Die Rekursion scheint nicht bis zur Größe eines Pixels hinab zu arbeiten (neudeutsch: herunterzubrechen). Bemutzt Du dazu den Rectangle-Befehl? Der ist m.E. schon ziemlich schnell.

Meinst Du das, dass das letzte Pixel rechts unten fehlt? Oder fehlen auf Deinem Computer auch im Bild einzelne Punkte? Dann stimmt an meiner Umsetzung was nicht und ich muss den Fehler suchen.

Um die neuen Pixel zu berechnen, benötige ich von dem jeweiligen Rechteck die Farbwerte der Randpunkte. Diese ermittle ich im Moment noch über die Farbnummern in der Palette und setze den neuen Punkt mit pixels[]. Und das bremst aus.
Ein Rechteck wird nicht gezeichnet, sondern nur dessen Eckpunkte als neuer Bereich gewählt.
Beste Grüße
Mathematiker


Delphi-Laie - Mo 10.09.12 23:05

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Meinst Du das, dass das letzte Pixel rechts unten fehlt? Oder fehlen auf Deinem Computer auch im Bild einzelne Punkte? Dann stimmt an meiner Umsetzung was nicht und ich muss den Fehler suchen.


Nein, da fehlt nichts, ich meine die Rekursion generell, wenn sie in der untersten Stufe angelangt ist. Das Bild ist nicht so feinpixelig, als daß ich den Eindruck hätte, daß sich die niedrigste Rekursionsstufe mit der Größe eines Pixels befassen würde. Nur mein erster Eindruck, ich sah nicht in den Quelltext.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Ein Rechteck wird nicht gezeichnet, sondern nur dessen Eckpunkte als neuer Bereich gewählt.


Ja, und wie wird eben dieser "Bereich" gefüllt - pixelweise oder per Rectangle-Befehl?

Das Prinzip, Rechtecke recht großzügig zu füllen, wenn alle 4 Eckpunkte die gleiche Farbe haben und damit anzunehmen, daß darin nichts anderes vorhanden ist (und sich damit aber eben auch manchmal zu irren), kenne ich von einem Fraktalzeichenprogramm (noch aus DOS-Zeiten). Füllt natürlich schnell, doch sollte sich doch ein kleines Subfraktalchen darin befinden, hat es keine Chance, erkannt und gezeichnet zu werden.


Mathematiker - Mo 10.09.12 23:12

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Das Bild ist nicht so feinpixelig, als daß ich den Eindruck hätte, daß sich die niedrigste Rekursionsstufe mit der Größe eines Pixels befassen würde.

Das wird wohl daran liegen, dass nur 256 verschiedene Farben zum Zeichnen verwendet werden. Diese Einschränkung ist aber notwendig, damit ich die Farben zyklisch austauschen kann.
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Ja, und wie wird eben dieser "Bereich" gefüllt - pixelweise oder per Rectangle-Befehl?

Es wird pixelweise gefüllt. Aber die Idee, die Punkte eines sehr kleinen Bereiches ohne Neuberechnung gleichartig zu füllen, werde ich testen. Danke für den Hinweis.
Beste Grüße
Mathematiker

Nachtrag: Ein erster Test, kleine Bereiche gleich zu färben, ergibt ein deutlich schlechteres Bild. Vor allem beim Tauschen der Farben sieht man das. Schade.


Sinspin - Di 11.09.12 08:09

Eal ob Du einzelne Pixel raussammelst und neu schreibst oder ganze Zeilen bearbeitest, ScanLine ist wesentlich schneller. Noch schneller wäre die Verwendung eines Rasters. Also ein von Dir selber angelegtes Stück Speicher was die Größe des Bildes hat und in dem Du das Bild aufbaust und es dann via Scanline hinkopierst wo Du es hinhaben willst.


Tranx - Di 11.09.12 08:44

Ich habe mal mit einem Wert gespielt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
  if anzahl mod 1000000 = 0 then  // hier statt 100
        begin
          paintbox1.canvas.draw(00, bitmap);
          application.processmessages;
        end;
...



dann wird das Bild zwar nicht schneller, aber auf einmal aufgebaut. Probier mal. Ansonsten liegt es wohl an dem, was mein Vorgänger schrieb.

P.S.: Die Routine "Farbmitte" wird ca. 2 Mio mal aufgerufen. Die Routine "Fenster" 390.000 mal. Zweitere hat etwa eine Prozedurzeit von 6 µs. Da stört schon sehr die Ermittlung der Prozedurzeit. Es käme allein dabei schon 2,4 s heraus. Auch erstere Prozedur benötigt ca. 0,2 µs pro Aufruf. Wenn überhaupt ist dort der Optimierungsbedarf.


Delete - Di 11.09.12 11:01

Ich bin gerade dabei, beim Programm "durchzublicken".
Der Aufbau wirkt auf mich chaotisch, die vielen begin - end ergeben vermutlich keinen Sinn und verlangsamen das Programm zusätzlich.


SvenAbeln - Di 11.09.12 11:50

Ich muss user profile iconhathor zustimmen, das Programm wirkt in der Form chaotisch, besonders die ganzen Globalen Variablen sollte man mal überdenken. Wenn man es aber korrekt formatiert und einrückt ergeben die begin-end auch einen Sinn. Es sind einige lokale Funktionen in lokalen Funktionen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.D1Click(Sender: TObject);
  procedure plasma;

    function _getpixel(x, y: integer): integer;
    begin [...] end;

    function farbmitte(f1, f2, abweich: integer): WORD;
    begin [...] end;

    [usw...]

  begin [...] end;   // von procedure plasma;
begin [...] end;    // von procedure TForm1.D1Click

Auch haben Begin - End keinen Einfluss auf die Performance des Programms, da diese nur zur Strukturierung des Delphi Code dienen und nicht im kompilierten Programm auftauchen.


Tranx - Di 11.09.12 12:16

Wie ich schon schrieb. Die Prozeduren Fenster (390000 mal) und Farbmitte (2 Mio mal) begrenzen schon die Schnelligkeit des Programms. Ich weiß nicht, ob da nicht durch weniger Aufrufe (geschicktere Programmierung) eine Beschleunigung möglich wäre. Dazu müsste man aber den genauen Algorithmus zur Farberzeugung haben. Vielleicht ist da was veränderbar. Denn selbst wenn eine Prozedur nur 0,1 µs dauern würde, wären das bei 2 Mio Aufrufe schon 0,2 s. Schlimmer noch, wenn sie dann 6 µs dauert. Und der Aufbau dauert insgesamt (bei meinem Rechner) 1,5 - 1,6 s.

Ich habe nochmals mit dem vorhergenden Wert gespielt:


Delphi-Quelltext
1:
2:
3:
4:
5:
        if anzahl mod n = 0 then
        begin
          paintbox1.canvas.draw(00, bitmap);
          application.processmessages;
        end;

n = 1 : Dauer ca. 274 s
n = 10 : Dauer ca. 27 s
n = 100 : Dauer ca. 2,7 s
n = 10000 : Dauer ca. 1,6 s
das (ca. 1,53 s) ist bei mir die Untergrenze. Die Draw-Prozedur scheint das Ganze sehr zu verlangsamen, je öfter sie aufgerufen wird.


Mathematiker - Di 11.09.12 13:44

Hallo,
Dank an Alle für die vielen Hinweise.
Zuerst habe ich den Quelltext entspechend Euer Hinweise umformatiert. Ich hoffe, dass er jetzt besser lesbar ist.
Es war aber kein begin oder end zuviel. Alle sind notwendig.
Die globalen Variablen habe ich unter private von TForm1 untergebracht.

user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Und der Aufbau dauert insgesamt (bei meinem Rechner) 1,5 - 1,6 s.
... Die Draw-Prozedur scheint das Ganze sehr zu verlangsamen, je öfter sie aufgerufen wird.

Gratulation zu Deinem Rechner. Bei mir sind es mehr als 6 s.
Den Aufruf der Draw-Prozedur habe ich auf 10000 Schritte geändert, wodurch es, wie Du gesagt hast, schneller wird. Viel mehr geht da aber nicht, denn ich möchte eigentlich, dass man auf Wunsch das Zeichnen auch relativ schnell abbrechen kann.

user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Dazu müsste man aber den genauen Algorithmus zur Farberzeugung haben.

Wenn oben P1, P2 und unten P3, P4 die Eckpunkte eines Rechtecks sind und n1 bis n4 deren Farbnummer in der Palette, wird wie folgt vorgegangen.
Der Mittelpunkt zwischen P1 und P3 des Rechtecks erhält die Farbe
farbnummer = (n1+n3)/2 + 1/2 Zufallsabweichung
wobei die Zufallsabweichung eine Zufallszahl aus dem Intervall [0, Rechteckhöhe] ist.
Für den Mittelpunkt P1P2 werden n1,n2 und Rechteckbreite, für P2P4 n2,n4 und Höhe, für P3P4 n3,n4 und Breite herangezogen.
Für den Mittelpunkt des Rechteck wird der Palettenindex als arithmetisches Mittel der 4 neuen Farbnummern ermittelt.
Klingt vielleicht etwas wirr, funktioniert aber.

Auf jeden Fall werde ich noch einmal prüfen, ob das Ersetzen von Pixels[] durch Scanline bei dem Hauptbitmap etwas bringt.

Beste Grüße
Mathematiker

Nachtrag: Ich habe alle Pixels[] durch Scanline ersetzt, z.B.

Delphi-Quelltext
1:
2:
3:
//bitmap.canvas.Pixels[xa,ym]:=vfarben[farbe1];
   rowrgb:=bitmap.ScanLine[ym];
   rowrgb[xa]:=prgbtriple(colorlist.items[farbe1])^;

mit dem Ergebnis, dass die Zeichnung das Mehrfache(!) an Zeit benötigt. Irgendetwas mache ich falsch.


mandras - Di 11.09.12 18:04

Zwei Dinge hier kosten richtig Zeit:
a) Scanline[..] ist eine sehr zeitintensive Funktion. Am einfachsten vor Ausführen von fenster
ein Array mit den Ergebnissen aller auftretenden Scanlines anlegen und dieses verwenden.
b) colorlist.items kostet auch viel Zeit da hier eine vollständige TLIST-Implementierung vorliegt. Am besten durch ein Array ersetzen.


Mathematiker - Di 11.09.12 19:05

Hallo Mandras,
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
Zwei Dinge hier kosten richtig Zeit:
a) Scanline[..] ist eine sehr zeitintensive Funktion. Am einfachsten vor Ausführen von fenster
ein Array mit den Ergebnissen aller auftretenden Scanlines anlegen und dieses verwenden.
b) colorlist.items kostet auch viel Zeit da hier eine vollständige TLIST-Implementierung vorliegt. Am besten durch ein Array ersetzen.

Genau das ist es :zustimm: .
Ich habe Scanline komplett entfernt und dafür ein Feld für die Farbnummern eingeführt. Ergebnis auf meinem Rechner: nur noch 1/3 der ursprünglichen Zeit. (siehe Rev 3)
Auf deutlich schnelleren Computern als meinem, kann in der procedure fenster auch das Kopieren des Zwischenergebnisses aller 10000 Schritte auf höhere Werte geändert werden. Das verkürzt die Berechnungszeit noch einmal.
Colorlist brauchte ich nur für die Farbänderung. Nach dem Entfernen blieb die Farbsimulaton gleich, dafür wurde der Quelltext einfacher. Auch nicht schlecht.

Beste Grüße
Mathematiker


Delphi-Laie - Di 11.09.12 22:46

Mathematiker, die Darstellung wirkt tatsächlich schneller, wenn man auf die gleichnamige Schaltfläche drückt. Worin die Beschleunigung liegt, ist mir nicht ganz klar, aber es scheint, als sei der Zugriff von VCL- auf Nicht-VCL-Elemente verlagert worden zu sein. Arrays sind jedenfalls Nicht-VCL-Elemente und ziemlich schnell.

Ich probierte mit dem Faktor und - siehe da - das Bild wird viel fein(pixelig)er, also wird tatsächlich bis zum Bildpunkt "heruntergebrochen".

Allerdings hatte ich beim Wählen einer Farbpalette und / oder beim Klick auf "Darstellung" nunmehr einen Fehler (Exeption 103?), bekomme ich jetzt leider nicht reproduziert.

Und wen man die Darstellung eines Bildes abbricht, so wird bei Wahl einer anderen Farbpalette das neue Bild nur soweit dargestellt, wie das Bild davor bis zum Zeitpunkt des Abbruches gezeichnet wurde. Soll das etwa so sein?


Mathematiker - Di 11.09.12 22:57

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Worin die Beschleunigung liegt, ist mir nicht ganz klar, aber es scheint, als sei der Zugriff von VCL- auf Nicht-VCL-Elemente verlagert worden zu sein. Arrays sind jedenfalls Nicht-VCL-Elemente und ziemlich schnell.

Es liegt wohl daran, dass das bisherige Scanline, auch wenn es schnell ist, praktisch für jedes Pixel zweimal aufgerufen wurde und immer die ganze Zeile liest. Da jetzt die Daten im Feld liegen und nur das Feldelement abgefragt wird, geht es deutlich schneller.

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings hatte ich beim Wählen einer Farbpalette und / oder beim Klick auf "Darstellung" nunmehr eine Fehler (Exeption 103?), bekomme ich jetzt leider nicht reproduziert.

Das ist nicht gut. Bisher war so etwas bei mir noch nicht aufgetreten. Da muss ich wohl viel testen.

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Und wen man die Darstellung eines Bildes abbricht, so wird bei Wahl einer anderen Farbpalette das neue Bild nur soweit dargestellt, wie das Bild davor bis zum Zeitpunkt des Abbruches gezeichnet wurde. Soll das etwa so sein?

Eigentlich nicht, es ergibt sich aber aus der Berechnungsroutine. Im Moment muss man (noch) den Darstellungsschalter erneut wählen.

Außerdem habe ich noch ein paar weitere Prozent an Geschwindigkeit gewonnen, durch Entfernung der _getpixel-Prozedur und einer veränderten Berechnung der farbe5. Vielleicht geht es sogar noch etwas schneller und dann gibt es die Revision 4.

Beste Grüße
Mathematiker

Nachtrag: In der Hoffnung, den Fehler beseitigt zu haben, füge ich im ersten Eintrag eine neue Version ein.


Tranx - Mi 12.09.12 07:46

Kleiner Zusatz: bringt zwar nicht viel, aber verkleinert die Funktion Farbmitte erheblich:


Delphi-Quelltext
1:
2:
3:
4:
    function farbmitte(f1, f2, abweich: integer): byte;
    begin
      Result := max(1,((f1 + f2 - abweich) div 2 + random(abweich)) and 255); // Unith Math in Uses mit einbinden!!
    end;


Max bewirkt, dass der größte der beiden Werte 1, oder der berechnete Wert übergeben wird.
and 255 ist klar: nur die Bits 0 - 7 werden berücksichtigt. D.h. aus einer Zahl >256 wird dann eine Byte-Zahl < 256;
"and 255" ist scheinbar etwas schneller als "mod 256".

div 2 durch shr 1 zu ersetzen bringt nichts, außer Laufzeitfehlern. Shr ist die Bitweise Verschiebung der Bits einer Integer-Zahl nach rechts. Entspricht eine Ganzzahldivision / 2.

Die ganzen Berechnungen habe ich einfach mal in eine zusammengefasst. Alle Abfragen falle weg und die lokalen Variablen.

Eine völlige Ersetzung des Fuktionsaufrufs durch die Berechnungsformel bringt nichts. Habe ich schon getestet. Eine weitere Verbesserung bringt m.E. nur eine Reduzierung der Berechnungsanzahl. Aber da wirst Du selber wohl keine Chance sehen, oder?

Nun sind es bei mir so zw. 0,90 und 0,92 s. Außerdem ist interessanterweise nun die Abhängigkeit von der Anzahl der Draw-Aufrufe nicht mehr ganz so dramatisch (zwischen 1000 und 100000) passiert fast nichts.

Komischerweise geht bei mir das Ganze in der Delphiumgebung schneller als beim Aufruf der Programmdatei (0,9 statt 0,98 s).


Mathematiker - Mi 12.09.12 13:28

Hallo Tranx,
user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Kleiner Zusatz: bringt zwar nicht viel, aber verkleinert die Funktion Farbmitte erheblich:

Delphi-Quelltext
1:
2:
3:
4:
    function farbmitte(f1, f2, abweich: integer): byte;
    begin
      Result := max(1,((f1 + f2 - abweich) div 2 + random(abweich)) and 255); // Unith Math in Uses mit einbinden!!
    end;

ich habe es gleich ausprobiert, mit dem Ergebnis, dass es etwas schneller wird. Nicht schlecht.
Interessant ist, dass Deine Formel nicht ganz die gleichen Werte liefert, wie meine.
(f1 + f2 - abweich) div 2 ist für einige Werte ungleich (f1 + f2) div 2 - abweich div 2.
Ich habe es getestet: In rund 8 % der Fälle ist Dein Wert größer und in 42 % kleiner. Dennoch entstehen Bilder, die kaum voneinander zu unterscheiden sind.

user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Komischerweise geht bei mir das Ganze in der Delphiumgebung schneller als beim Aufruf der Programmdatei (0,9 statt 0,98 s).

Das ist mir noch gar nicht aufgefallen. Aber, Du hast recht. So etwas hatte ich noch nie.
Erste Idee war, die windowsxp.res zu entfernen. Die hat ja ohnehin nur kosmetischen Charakter. Und sofort ist bei mir wieder alles normal: Die Programm-Exe läuft ohne Delphiumgebung wieder schneller, wie es sich gehört.

Beste Grüße
Mathematiker


Tranx - Mi 12.09.12 14:20

Der Unterschied kommt möglicherweise durch den Unterschied:

a div 2 + b div 2 <> (a + b) div 2,

da im linken Fall erst die Ganzzahloperation der Einzelergebnisse erfolgt und dann die Addition, während beim anderen Fall die Addition zuerst erfolgt und dann die Ganzzahloperation

3 div 2 + 5 div 2 = 1 + 2 = 3, (3+5) div 2 = 8 div 2 = 4


Mathematiker - Mi 12.09.12 17:16

Hallo,
durch ein paar kleinere Änderungen, z.B. copyrect statt draw, dürfte die Konstruktion des Bildes noch etwas schneller sein (siehe Rev 5).
Mein Eindruck ist, dass nun eine andere Grundidee notwendig ist, um eine weitere deutliche Beschleunigung zu erzielen.
Vielleicht sieht auch jemand von Euch eine Möglichkeit, die Berechnung der Farbmitte durch Assembler zu beschleunigen. Leider habe ich keine Ahnung von ASM.

Beste Grüße
Mathematiker


Horst_H - Mi 12.09.12 17:42

Hallo,

ist nicht die Funktion random nicht die Wurzel allen Übels in der Funktion Farbmitte?
Einfach mal weglassen und testen.

Gruß Horst


Tranx - Mi 12.09.12 17:49

Horst_H, ohne Random kommen keine zufälligen Abweichungen zustande. Aber random ist nicht so verlangsamend (statt 0,89 s ohne sind es 0,91 s mit Random). Allerdings ist die Darstellung ohne Random alles andere als plasmaähnlich). Das Problem sind die 2 Mio Aufrufe. Das sind bei 0,9 s ja bloß 0,45 µs pro Aufruf. (Mal so global berechnet. Es sind ja noch andere Prozeduren da)


Mathematiker - Mi 12.09.12 18:38

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
ist nicht die Funktion random nicht die Wurzel allen Übels in der Funktion Farbmitte?
Einfach mal weglassen und testen.

Die Bilder sehen ganz lustig aus, die ohne Random entstehen. Leider geht's ohne Zufallszahlen wirklich nicht.
Aber vielleicht ist das Bestimmen der Zufallszahlen doch veränderbar.
Schon bei meinen aller ersten Versuchen hatte mich gestört, dass ich eine Zufallszahl aus dem Intervall [-abweich/2 , +abweich/2] nur bestimmen konnte durch die merkwürdige Konstruktion

Delphi-Quelltext
1:
random(abweich) - abweich div 2                    

Halbiere ich von Anfang an die Variable und suche im Bereich [-abweich, abweich], so wird es mit

Delphi-Quelltext
1:
random(2*abweich) - abweich                    

auch nicht schneller. Die Operationen div 2 oder mit 2 multiplizieren sind scheinbar gleich schnell.
Ich habe auch schon versucht über real-Zahlen, also nur mit random, zu arbeiten. Dort bremst das dann notwendige round aus.

Vielleicht gibt es ein bessere Methode eine zufällige Zahl im genannten Intervall zu ermitteln. Ich grüble schon länger darüber nach, leider ohne Erfolg. :nixweiss:
Ein superschneller Pseudozufallsgenerator könnte vielleicht helfen?

Beste Grüße
Mathematiker

Nachtrag: Ich habe versucht den im Forum http://www.entwickler-ecke.de/viewtopic.php?t=77045&highlight=zufallszahl+asm beschriebenen Zufallsgenerator einzubauen.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function randomrange(a,b: integer): integer;
    asm
      push eax
      xchg eax, edx
      sub eax, edx
      imul edx, [randseed], $08088405
      inc edx
      mov [randseed], edx
      mul edx
      pop eax
      add eax, edx
    end;

Die Geschwindigkeit bleibt vorerst etwa gleich, da ich an a und b -abweich div 2 und +abweich div 2 übergeben muss.
Könnte man nur einen Wert übergeben, so könnte es vielleicht schneller werden.
Aber ohne ASM-Kenntnisse weiß ich nicht einmal, wo eigentlich die Variable b steckt? a ist wohl in eax.
Es wird sich wohl nicht vermeiden lassen. Ich muss mich mit ASM beschäftigen. :bawling:

Nachtrag 2: Ich hab es durch Versuch und Irrtum!

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
    function randomrange(a: integer): integer;
    asm
      shr eax, 1
      mov edx, eax
      neg eax
      push eax
      xchg eax, edx
      sub eax, edx
      imul edx, [randseed], $08088405
      mov [randseed], edx
      mul edx
      pop eax
      add eax, edx
    end;

ermittelt eine Zufallszahl im Intervall [-a/2,a/2]. Leider ist der Gesamtzeitgewinn nur unwesentlich.


Mathematiker - Mi 12.09.12 21:29

Hallo,
meine ASM-Versuche machen ja richtig Spaß. :rofl: Ich habe die alte farbmitte-Prozedur

Delphi-Quelltext
1:
2:
3:
4:
    function farbmitte(f1,f2,abweich:integer):byte;
    begin
      farbmitte:=max(1,((f1+f2) div 2+ random(abweich)-abweich div 2mod 256);
    end;

durch

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
    function farbmitte(f1,f2,abweich:integer):byte;
      function randomrange(f1,f2,a: integer): integer;
      asm
        add eax, edx
        shr eax, 1
        push eax
        mov eax, ecx
        shr eax, 1
        mov edx, eax
        neg eax
        push eax
        xchg eax, edx
        sub eax, edx
        imul edx, [randseed], $08088405
        mov [randseed], edx
        mul edx
        pop eax
        add eax, edx
        pop edx
        add eax, edx
//      mov ecx, 256
//      div ecx
//      mov eax, edx
      end;
    begin
      farbmitte:=max(1,randomrange(f1,f2,abweich) mod 256);
    end;

ersetzt und man spart schon etwas Zeit (rund 5 %). Schön wäre nun, wenn ich auch gleich noch mod 256 durchführen könnte.
Mein Versuch mit den 3 mit // gekennzeichneten Zeilen funktioniert nicht richtig. Oder gibt es auch unter ASM einen Mod-Befehl? In meiner Befehlsliste habe ich keinen gefunden.
Auch die Idee mit

Delphi-Quelltext
1:
and eax, 255                    

an Stelle der 3 Zeilen bringt nicht das Gewünschte.
Ganz toll wäre natürlich, wenn die Assemblerroutine auch gleich noch das Maximum bestimmt. Aber da habe ich nun gar keine Ahnung mehr.

Beste Grüße
Mathematiker


Horst_H - Do 13.09.12 11:11

Hallo,

etwas offtopic, oben auf dem Rechner läuft die angehängte EXE rev5 1,4 Sekunden unter Win7 und unter Linux mittels wine 1.5.2 in nur ( auch gefühlt ) 0,75 in Sekunden.
Mit Lazarus 0.93/fpc 2.6.1 ( es gibt jetzt Version 1.0 ) kompiliert ( dazu habe ich scanline aus dem Farbwechsel rausgeworfen und .canvas.pixels genutzt, was so langsam ist das eine CPU uner Volllast läuft ) sind es unter Win7 auch 0.8 Sekunden.
Wie wäre ein Test, wieviel canvas.pixels ausmacht?
Also in der procedure fenster alle
bitmap.canvas.Pixels[xe,ym]:=???;
auszukommentieren.
Man sieht zwar nichts, aber die Zeiten sind ja interessant.
Man könnte bei ausreichendem Zeitunterschied ja statt der Bitmap ein eigenes Feld nutzen und dieses dann kopieren.
Zudem müßte ein 32-bit Format für die Farben schneller sein.
"Früher" hat man für ein VGA-Plasma ein 8 Bit Format gehabt und auf dem VideoChip die Palette geändert.
http://de.wikibooks.org/wiki/Interrupts_80x86/_INT_10
Farbpalatte festlegen [http://de.wikibooks.org/wiki/Interrupts_80x86/_INT_10#Funktion_0Bh:_Farbpalette_f.C3.BCr_Grafikmodus_festlegen_.28EGA.2FVGA.29]
Das muss auch immer noch gehen.
Siehe wincrt/winGraph von Stefan Berinde http://math.ubbcluj.ro/~sberinde/wingraph/main.html macht das ja auch.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
Aus wingraph 
procedure SetAllPalette(var palette);
var pe: array of PALETTEENTRY;
     i: longint;
begin
  grResult:=grOK;
  if not(grEnabled) then begin
                           grResult:=grNoInitGraph;
                           Exit;
                         end;
  if not(palExist) then begin
                          grResult:=grNoPalette;
                          Exit;
                        end;
  with PaletteType(palette) do
  begin
    if (size > maxColors) then begin
                                 grResult:=grInvalidParam;
                                 Exit;
                               end;
    SetLength(pe,size);
    for i:=0 to size-1 do with pe[i],colTable[i] do
    begin
      peRed:=GetRValue(colors[i]); peGreen:=GetGValue(colors[i]); peBlue:=GetBValue(colors[i]);
      peFlags:=0;
      rgbRed:=peRed; rgbGreen:=peGreen; rgbBlue:=peBlue; rgbReserved:=0;
    end;
    for i:=size to maxColors-1 do with pe[0],colTable[i] do
    begin // all non-used bitmap palette entries equals the first entry
      rgbRed:=peRed; rgbGreen:=peGreen; rgbBlue:=peBlue; rgbReserved:=0;
    end;

    EnterCriticalSection(protect_devices);
    ResizePalette(grPalette,size);
    SetPaletteEntries(grPalette,0,size,pe[0]);
    SetDIBColorTable(grMemory,0,maxColors,colTable[0]);
    MapPaletteColors;
    RealizePalette(grWindow); RealizePalette(grMemory); RealizePalette(grTemp);
    LeaveCriticalSection(protect_devices);

    InvalidateRect(grHandle,nil,false);
    palSize:=size;
    SetLength(pe,0);
  end;
end;


Gruß Horst


Mathematiker - Do 13.09.12 13:47

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Wie wäre ein Test, wieviel canvas.pixels ausmacht?
Also in der procedure fenster alle
bitmap.canvas.Pixels[xe,ym]:=???;
auszukommentieren.

Geniale Idee :zustimm: und siehe da, die Berechnungszeit schrumpft auf weniger als 10%. Selbst meine alte "Mühle" braucht nun nur noch 0,1 s. Das Bild erscheint nahezu sofort! Besser geht es kaum noch. In der Rev 6 habe ich die Änderung sofort untergebracht.
Wenn ich jetzt noch mein ASM-Problem erfolgreich löse, dürfte es kaum noch schneller gehen.

Beste Grüße
Mathematiker


Horst_H - Do 13.09.12 13:54

Hallo,

Revision 6 hat wohl seine Macken.
Soviele Zugriffe auf Adresse $00 in so kurzer Zeit hatte ich noch nie....

Gruß Horst
P.S.
Ohne Canvas.Pixels schreibt man eigentlich auch keine Farbe an irgendeine Stelle, es ging nur darum zu zeigen, was die wahre Bremse ist.


Mathematiker - Do 13.09.12 13:57

user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Revision 6 hat wohl seine Macken.
Soviele Zugriffe auf Adresse $00 in so kurzer Zeit hatte ich noch nie....

Tut mir leid und verstehe ich auch nicht. Bei mir läuft es einwandfrei. Ich werde testen!
Beste Grüße
Mathematiker

Nachtrag: Ich habe plasma.zip im ersten Eintrag, ohne meine stümperhaften ASM-Versuche, die offensichtlich nicht funktionieren, angefügt.
Noch einmal: Tut mir leid.


Tranx - Do 13.09.12 14:11

Ich glaube kaum, dass Assemblerroutinen viel bringen, da der Compiler ja eh den Code optimiert. Da werden dann schon die entsprechenden Assemblerschritte erzeugt. Irgendwo habe ich mal gelesen, dass auch die Registerverwendung wenig bringt, da der Compiler auch hier diese optimiert. Da entstehen dann oft nur Fehler, weil irgendwelche Sprünge im Nirwana landen. Ich würde wegen der 5% da keine weitere Anstrengung unternehmen. Wenn es möglich wäre, die Aufrufe z.B. von Farbmitte und Fenster drastisch zu reduzieren, bringt das sicher wesentlich mehr.


Mathematiker - Do 13.09.12 14:19

Hall Tranx,
user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Ich glaube kaum, dass Assemblerroutinen viel bringen, da der Compiler ja eh den Code optimiert. Da werden dann schon die entsprechenden Assemblerschritte erzeugt.

Ich lasse mich gern überzeugen. Es war ein erster (erfolgloser) Versuch mit ASM. Ich habe es zwar noch im Text stehen, rufe es aber in Rev 7 nicht mehr auf. Und bei der jetzt erreichten Geschwindigkeit ist es wohl auch nicht mehr notwendig.
Beste Grüße
Mathematiker


Gammatester - Do 13.09.12 15:51

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Es war ein erster (erfolgloser) Versuch mit ASM. Ich habe es zwar noch im Text stehen, rufe es aber in Rev 7 nicht mehr auf.
Hallo, das and eax,255 als Ersatz für mod 256 ist schon richtig!

Ich weiß zwar nicht, was genau bei Deinem ASM-Versuch erfolglos war, aber Deine Funktion erzeugt auf jeden Fall keine random-komptatible Zahlen. Delphi verwendet einen Linearen Kongruenzgenerator [http://de.wikipedia.org/wiki/Kongruenzgenerator#Linearer_Kongruenzgenerator], und zwar im wesentlichen randseed := (randseed * $08088405 + 1) mod 2^32. Bei Dir fehlt der wichtige (+ 1)-Teil, und durch das Weglassen wird der Generator zur programmierten Katastrophe; in dem von Dir zitierten Kodeschnipsel ist er (via inc edx) noch vorhanden.

Einen Versuch mit der wiedereingebauten verlorenen Zeile solltest Du allemal machen, und sei's nur im zu sehen, daß das der Grund für die Erfolglosigkeit war.

Gruß Gammatester


Horst_H - Do 13.09.12 16:37

Hallo,

ich habe einen Fehler begangen und einfach nur die EXE extrahiert und gestartet.Dadurch fehlte die Datei palette.c00 und deshalb erst IO-Fehler 103 und dann die nicht enden wollenden Zugriffsverletzungen.
Liegt also nicht am ASM-Teil sondern nur an meiner Bequemlichkeit :oops:

Jetzt erstellt das Programm ratz-fatz in knapp 0,07 Sekunden eine neue Darstellung.
Die Zeile

Delphi-Quelltext
1:
 if anzahl mod 50000 = 0 then application.processmessages;                    

kann man sich sparen.Nur echte Gamer sind so schnell.
Statt

Delphi-Quelltext
1:
2:
3:
anzahl mod 50000 = 0 then
lieber etwas wie 
anzahl AND ( (1 shl 16) - 1) = 0 // AND 65535 == Mod 65536

Mein AMD dividiert ungern.
Gruß Horst.


Delphi-Laie - Do 13.09.12 16:43

Die Geschwindigkeitsfortschritte dieses Programmes sind atemberaubend.

Leider mußte ich mich schon sehr bald mit meinen nicht allzu hilfreichen Hinweisen ausklinken.

Eine Ergonomieangelegenheit fiel mir noch auf: Trotz der deutlich schnelleren Geschwindikeit gibt es ggf. immer noch eine spürbare Verzögerung zwischen dem Aufrufen der Combobox für die Farbpalette und dem Darstellen des Bildes. Klickt man währenddessen im Eifer des Gefechtes auf den Druckschalter "Darstellung" (weil man z.B. annimmt, daß das erforderlich sei), so wird nach Erscheinen des Bildes dieses dann zweimal gezeichnet. Vielleicht könnte man während des Bildaufbaus den Druckschalter "Darstellung" unbedienbar machen?

Daß es für mod noch einen schnelleren Ersatz gibt, wußte ich bis dato nicht. Immerhin wird diese Funktion n.m.W. 1:1 als Maschinencoe übersetzt. Verstehe nicht, daß es da keine "prozessorinterne" Optimierung gibt. Schließlich gibt es sozusagen zwei Funktionen für das gleiche.

Sollte der Assembler-Integer-Zufallsgenerator es wirklich zur "Serienreife" schaffen, rege ich an, ihn separat in der Bibliothek zu veröffentlichen.


Mathematiker - Do 13.09.12 18:02

Hallo Gammatester,
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Bei Dir fehlt der wichtige (+ 1)-Teil, und durch das Weglassen wird der Generator zur programmierten Katastrophe; in dem von Dir zitierten Kodeschnipsel ist er (via inc edx) noch vorhanden.

Danke für den Hinweis. Es ist typisch für mich! Beim Abtippen habe ich eine Zeile übersehen. Aber das kommt davon, wenn man doch nicht so richtig weiß, was bei Assembler passiert.
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Einen Versuch mit der wiedereingebauten verlorenen Zeile solltest Du allemal machen, und sei's nur im zu sehen, daß das der Grund für die Erfolglosigkeit war.

Habe ich gleich gemacht. Im Ergebnis habe ich nun

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
function farbmitte(f1,f2,abweich: integer): integer;
    asm
         add eax, edx       //(f1+f2) div 2
         shr eax, 1
         push eax           //(f1+f2) div 2 speichern
         xor eax, eax       //Initialisierung auf 0 bis abweich
         mov edx, ecx
         push eax           //Zufallsgenerator
         xchg eax, edx
         sub eax, edx
         imul edx, [randseed], $08088405
         inc edx
         mov [randseed], edx
         mul edx
         pop eax
         add eax, edx       //in eax steht zufallswert [0,abweich-1] , getestet!!!
         shr ecx, 1         //abweich div 2
         sub eax, ecx       //subtraktion abweich div 2
         pop edx            //(f1+f2) div 2 zurückholen
         add eax, edx       //Addition (f1+f2) div 2
         and eax, 255       //mod 256
         cmp eax, 0         //Test auf größer Null
         jna @null
         ret
         @null: mov eax, 1
       end;

Irgendwie funktioniert es schon und es ist auch schneller. Dennoch sehen die entstehenden Abbildungen etwas anders aus, als bei

Delphi-Quelltext
1:
2:
3:
4:
    function farbmitte(f1,f2,abweich:integer):byte;
    begin
      result:=max(1,((f1+f2) div 2 + random(abweich) - abweich div 2mod 256);
    end;

Mit Assembler entstehen mitunter merkwürdige, kleine farbige Bereiche, ohne nicht. Irgendwo steckt also noch ein Fehler.

Hallo Horst_H,
es freut mich, dass es doch funktoniert.

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Die Geschwindigkeitsfortschritte dieses Programmes sind atemberaubend.

Ich hätte nie gedacht, dass es so schnell wird, von ursprünglich gut 8 Sekunden auf nun weniger als 0,1 Sekunden (auf meinem PC).
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Trotz der deutlich schnelleren Geschwindikeit gibt es ggf. immer noch eine spürbare Verzögerung zwischen dem Aufrufen der Combobox für die Farbpalette und dem Darstellen des Bildes. Klickt man währenddessen im Eifer des Gefechtes auf den Druckschalter "Darstellung" (weil man z.B. annimmt, daß das erforderlich sei), so wird nach Erscheinen des Bildes dieses dann zweimal gezeichnet. Vielleicht könnte man während des Bildaufbaus den Druckschalter "Darstellung" unbedienbar machen?

Ich habe schon etwas geändert. Mal sehen, was noch machbar ist.

In der Rev 8 (schon wieder eine!) sind beide farbmitte-Methoden (ohne und mit ASM) enthalten. Voreingestellt habe ich im Moment noch ohne Assembler.
Das 2.Bitmap habe ich im Text entfernt. Man braucht es nicht mehr. Außerdem kann man die Größe des Programmfensters jetzt ändern.

Beste Grüße
Mathematiker


OlafSt - Fr 14.09.12 23:52

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function farbmitte(f1,f2,abweich: integer): integer;
    asm
         [...]
         sub eax, ecx       //subtraktion abweich div 2
         pop edx            //(f1+f2) div 2 zurückholen
         add eax, edx       //Addition (f1+f2) div 2
         and eax, 255       //mod 256
         cmp eax, 0         //Test auf größer Null
         jna @null
         ret
         @null: mov eax, 1
       end;



Ich hätte da eine winzige Optimierung anzubieten, was diesen Assemblerteil angeht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function farbmitte(f1,f2,abweich: integer): integer;
    asm
         [...]
         sub eax, ecx       //subtraktion abweich div 2
         pop edx            //(f1+f2) div 2 zurückholen
         add eax, edx       //Addition (f1+f2) div 2
         and eax,255        //AND setzt bereits Zeroflag, wenn EAX 0 wird
         cmovnz eax,1       //Conditional Move
         ret
       end;


Sollte der Conditional Move falsch herum sein, einfach in CMOVZ umändern ;) CMOVxx gibts seit dem 80486, sollte also auch auf alten Maschinen problemlos laufen und auch von alten Delphi-Compilern übersetzbar sein.

Was die Abweichungen mit/Ohne ASM angeht: Mir war so, als wäre der div-Operator noch unterhalb der Additions- und Subtraktionsoperatoren angesiedelt. Ich habe nicht den Eindruck, das das richtig in der ASM-Routine berücksichtigt ist.

Aber ich mag hier irren, es ist Jahre her, das ich in der Gegend im Helpfile unterwegs war.


Horst_H - Sa 15.09.12 06:58

Hallo,

Ich habe neben der Zeit auch anzahl ausgegeben und war erstaunt.
Die Anzahl der Berechnungen fand ich verbesserungswürdig.

Farbtupfer steht als Ersatz für eine Berechnung:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
program unbenannt;

var 
  AnzahlBerechnungen,
  Farbtupfer: longInt;

  procedure fensterOrg(xa,xe,ya,ye:integer);
    var 
        xm,ym,dx,dy:integer;
    begin
          inc(AnzahlBerechnungen);
          xm:=(xa+xe) div 2;
          ym:=(ya+ye) div 2;
          
          if xa>0 then
             else
          begin
             inc(Farbtupfer);
          end;

          if ya>0 then
          else
          begin
             inc(Farbtupfer);
          end;

             inc(Farbtupfer);

             inc(Farbtupfer);

             inc(Farbtupfer);

          //rekursive Konstruktion
          if xm>xa then
          begin
             fensterOrg(xa,xm,ya,ym);
             fensterOrg(xa,xm,ym,ye);
          end;
          if xe>succ(xm) then
          begin
             fensterOrg(xm,xe,ya,ym);
             fensterOrg(xm,xe,ym,ye);
          end;
     end;
// Neue Version mit fruehzeitigem Abbruch und nur den notwendigen Berechnungen
    procedure fenster(xa,xe,ya,ye:integer);
    var 
        xm,ym,dx,dy:integer;
        bDx,bDy : boolean;
    begin
          dx:=(xe-xa);
          dy:=(ye-ya);
          bDx := dx > 1;
          bDy := dy > 1;
          //if Not(bDx) AND Not(bDy)  then 
          if Not(bDx OR bDy)  then 
            exit;

          inc(AnzahlBerechnungen);    
          //Diamond-Square-Algorithmus
 
          xm:=(xa+xe) div 2;
          ym:=(ya+ye) div 2;
          IF xa = 0 then
            inc(farbtupfer);    
          IF ya = 0 then
            inc(farbtupfer);
          
{   Nur bei dy > 1 ergibt sich ein neues ym           
        farbe3:=farbmitte(pixel2,pixel4,dy);
          farbfeld[xe,ym]:=farbe3;
}

          IF bdy then
            inc(farbtupfer,1);
            
{  Nur bei dx > 1 ergibt sich ein neues xm
         farbe4:=farbmitte(pixel3,pixel4,dx);
          farbfeld[xm,ye]:=farbe4;        
}

          IF bdx then
            inc(farbtupfer);
{Entsprechendes hier
          farbe5:=((farbe1+farbe3) div 2 + (farbe2+farbe4) div 2) div 2;
          farbfeld[xm,ym]:=farbe5;          
}

          IF bdx AND bdy then
            inc(farbtupfer);

          //rekursive Konstruktion
          
          fenster(xa,xm,ya,ym);
          fenster(xa,xm,ym,ye);
          fenster(xm,xe,ya,ym);
          fenster(xm,xe,ym,ye);

     end;
const 
  width= 620;
  height = 568;
BEGIN
  AnzahlBerechnungen := 0;
  farbtupfer:= 4//Startwerte   

  fenster (0,width-1,0,height-1);  
  writeln(' Anzahl Pixel             :',width*height);
  writeln(' Anzahl Rekursionen       :',AnzahlBerechnungen);  
  writeln(' Anzahl berechneter Pixel :',  FarbTupfer);

  AnzahlBerechnungen := 0;
  farbtupfer:= 4//Startwerte   
  writeln;
  
  writeln('Original Plasma 8');
  fensterOrg (0,width-1,0,height-1);  
  writeln(' Anzahl Pixel             :',width*height);
  writeln(' Anzahl Rekursionen       :',AnzahlBerechnungen);  
  writeln(' Anzahl berechneter Pixel :',  FarbTupfer);
END.

Mit der Ausgabe:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
 Anzahl Pixel             :352160
 Anzahl Rekursionen       :164440
 Anzahl berechneter Pixel :352160

Original Plasma 8
 Anzahl Pixel             :352160
 Anzahl Rekursionen       :382805
 Anzahl berechneter Pixel :1150530


Es werden nur noch knapp 1/3 Berechnungen benötigt.
Anzahl Rekursionen ist der falsche Ausdruck, es sollte Anzahl Berechnugsdurchläufe heißen.
Meine Variante wird 822201 fach aufgerufen aber läuft nur in 164440 durch.

Ich habe es nicht mit Lazarus getestet, vielleicht morgen

Gruß Horst
faktor 0 sieht sweet aus.


Mathematiker - Sa 15.09.12 08:55

Hallo OlafSt,
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Ich hätte da eine winzige Optimierung anzubieten, was diesen Assemblerteil angeht:
...
Sollte der Conditional Move falsch herum sein, einfach in CMOVZ umändern ;) CMOVxx gibts seit dem 80486, sollte also auch auf alten Maschinen problemlos laufen und auch von alten Delphi-Compilern übersetzbar sein.
Was die Abweichungen mit/Ohne ASM angeht: Mir war so, als wäre der div-Operator noch unterhalb der Additions- und Subtraktionsoperatoren angesiedelt. Ich habe nicht den Eindruck, das das richtig in der ASM-Routine berücksichtigt ist.

Danke für den Hinweis. Es gibt nur bei mir ein kleines Problem. Mein Delphi 5 meckert sowohl bei cmovz als auch cmovnz mit dem Kommentar "Syntaxfehler".
In meiner ASM-Befehlsliste für Delphi steht dieser Befehl auch nicht drin. Könnte es sein, dass er erst in späteren Delphi-Versionen richtig erkannt wird?
Schön wäre es, wenn es funktionieren würde, denn die Berechnung würde wohl noch schneller ablaufen.

Den 2.Hinweis werde ich genauer überprüfen. Ich habe Klammern gesetzt, so dass es jetzt

Delphi-Quelltext
1:
      result:=max(1,(((f1+f2) div 2) + random(abweich) - (abweich div 2)) mod 256);                    

wird; mit dem weiterhin richtigen Ergebnis. Eigentlich dachte ich genau das in der Assembler-Routine umgesetzt zu haben. Offensichtlich aber nicht.

Meine Überlegung war, dass während der Berechnung ja auch negative Zahlen vor dem mod auftreten können. Delphi gibt nach mod einen negativen Wert zurück, der dann durch max richtig zu 1 wird.
Ich weiß nun nicht, ob auch im Assembler-Text das ebenso geschieht.
Vor vielen, vielen Jahren hatte ich etwas mit Assembler am U880 zu tun und errinere mich nur noch schwach, dass es da Probleme mit negativen Zahlen gab und man irgendwelche Flags abfragen musste.

Hallo Horst_H,
Danke für Deine neue Idee. Allerdings muss ich das erst einmal richtig verstehen. Ich melde mich wieder, so bald ich es ausprobiert habe.
Verwirrt bin ich nur, dass scheinbar die Prozedur Fenster nicht mehr aufgerufen wird. Jedenfalls markiert mein Compiler die Prozedur nach dem Übersetzen nicht mit den berühmten Pünktchen an der Seite.

Nachtrag: Ich glaube, Deine Idee verstanden zu haben und es geht erneut schneller. Lustiger Nebeneffekt ist, dass die Tests

Delphi-Quelltext
1:
2:
          bdx := dx>1;
          bdy := dy>1;

nun bei Faktor = 1 geändert werden müssen auf >0. Setzt man >2, dann funktioniert es ab Faktor = 4.

Beste Grüße
Mathematiker

Nachtrag 2: Angeregt von der Horst_H-Idee die Berechnungen zu zählen, konnte ich diese durch Einfügen von

Delphi-Quelltext
1:
 if (xe-xa<=1and (ye-ya<=1then exit;                    

als 1.Zeile der Fensterprozedur deutlich verringern. Dennoch sind immer noch einige zu viel.
Da es eine wichtige Änderung ist, füge ich dies als Rev 9 in den ersten Eintrag ein.


Mathematiker - Sa 15.09.12 12:06

Hallo,
ich hab's gefunden und bin etwas stolz auf mich. :P
Die Assembler-Routine farbmitte läuft einwandfrei:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
    function farbmitte(f1,f2,abweich: integer): integer;
    //max(1,((f1+f2) div 2 + random(abweich) - abweich div 2) mod 256)
    asm
         add eax, edx       //(f1+f2) div 2
         shr eax, 1
         cmp ecx, 1
         jbe @ohnea         //abweich < 2
         push eax           //(f1+f2) div 2 speichern
         xor eax, eax       //Initialisierung auf 0 bis abweich
         mov edx, ecx
         push eax           //Zufallsgenerator
         xchg eax, edx
         sub eax, edx
         imul edx, [randseed], $08088405
         inc edx
         mov [randseed], edx
         mul edx
         pop eax
         add eax, edx       //in eax steht zufallswert [0,abweich-1]
         pop edx            //(f1+f2) div 2 zurückholen
         add eax, edx       //Addition (f1+f2) div 2
         shr ecx, 1         //abweich div 2
         sub eax, ecx       //subtraktion abweich div 2
         jc @null
         @ohnea: and eax, 255       //mod 256
         cmp eax, 0         //Test auf Null
         jz @null
         ret
         @null: mov eax, 1
       end;

Ich musste erst (f1+f2) div 2 addieren und danach die halbe Abweichung subtrahieren. Damit ist bei einem Überlauf das Carry-Flag gesetzt und ich kann korrekt springen.
Ergebnis: Die Berechnung eines Bildes wird noch einmal schneller. Auf meinem PC dauert es nun höchstens 0,72 s, mitunter, je nach Zufallswerten, auch noch weniger.

Mittlerweile habe ich auch, zumindest für ungefähr quadratische Zeichenbereiche, die überflüssigen Berechnungen beseitigen können. d.h. die Horst_H-Idee umgesetzt. Der Geschwindigkeitsgewinn war nicht so groß, aber jetzt ist der Algorithmus exakter. Warum bei größeren Unterschieden von Breite und Höhe doch wieder mehr Berechnungen auftreten, weiß ich noch nicht. Die neue Version ist in der Revision 10.

Beste Grüße
Mathematiker


Mathematiker - Sa 15.09.12 23:28

Hallo,
eine weitere Änderung der Assemblerroutine sorgt dafür, dass sie schneller durchlaufen wird. Da der Bereich der Zufallszahlen aus dem Intervall [0, abweich-1] bestimmt wird, können einige Schritte eingespart werden:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
    function farbmitte(f1,f2,abweich: integer): integer;
    //max(1,((f1+f2) div 2 + random(abweich) - abweich div 2) mod 256)
    asm
         add eax, edx       //(f1+f2) div 2
         shr eax, 1
         cmp ecx, 1
         jbe @ohnea         //abweich < 2
         push eax           //(f1+f2) div 2 speichern
         xor edx, edx       //Zufallsgenerator
         mov eax, ecx       //Initialisierung auf 0 bis abweich
         imul edx, [randseed], $08088405
         inc edx
         mov [randseed], edx
         mul edx
         mov eax, edx       //in eax steht zufallswert [0,abweich-1]
         pop edx            //(f1+f2) div 2 zurückholen
         add eax, edx       //Addition (f1+f2) div 2
         shr ecx, 1         //abweich div 2
         sub eax, ecx       //subtraktion abweich div 2
         jc @null
         @ohnea: and eax, 255       //mod 256
         cmp eax, 0         //Test auf Null
         jz @null
         ret
         @null: mov eax, 1
       end;

Gesamtstand: Von ursprünglich 8,4 s je Bild nun nur noch 0,062 s auf meinem PC. Das ist 135 mal so schnell und war nur durch die Hilfe von Euch möglich. Danke.
Vielleicht schaffen wir ja noch mehr? :)

Beste Grüße
Mathematiker


Delphi-Laie - So 16.09.12 10:02

Die Größe des Programmfensters war in der vorvorigen Version laut Deiner Änderungsliste veränderbar, nun aber nicht mehr. Verpaßte ich, mir diese Version zu besorgen. Hat das einen Grund, daß diese Funktion wieder verschwand?


Mathematiker - So 16.09.12 10:10

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Die Größe des Programmfensters war in der vorvorigen Version laut Deiner Änderungsliste veränderbar, nun aber nicht mehr. Verpaßte ich, mir diese Version zu besorgen. Hat das einen Grund, daß diese Funktion wieder verschwand?

Tut mir leid. Ich habe wohl nicht die richtige "letzte" Version angefügt.
Ist schon in Rev 11 geändert.
Beste Grüße
Mathematiker


Delphi-Laie - So 16.09.12 10:22

Das Programm wird immer schöner.

Bezüglich Geschwindigkeitserhöhung habe ich leider nichts beizutragen, wohl aber bezüglich Programmergonomie (oder auch "-intelligenz"): Beim Starten und beim Größenändern des Formulars erscheint das Bild nicht von allein, es muß sich mit "Darstellung" besorgt werden. Könnte man diese Ereignisse nicht für eine automatische Darstellung verarbeiten? Müßte nach meiner bescheidenen Erfahrung mit Delphi nicht allzu schwierig sein.

Ergänzung: Beim Größenändern wäre evtl. ein Zeitverzug vonnöten, ansonsten, konkret bei "manuellem" Resize, würde das Bildzeichnen vielleicht zu oft aufgerufen werden. Vor dem gleichen Problem stand ich bei meinem Sortieranimationsprogramm beim Bedienen der Spinedits und konnte dank Hilfe lösen. Ohne Zeitverzug verfing sich mein Programm irgendwann in irgendeiner Endlosschleife.


Mathematiker - So 16.09.12 10:45

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Könnte man diese Ereignisse nicht für eine automatische Darstellung verarbeiten? Müßte nach meiner bescheidenen Erfahrung mit Delphi nicht allzu schwierig sein.

Danke für den Hinweis.
Ich habe es sofort eingebaut, in dem ich die paintboxpaint-Methode mit der Schalter-Methode verbunden habe. Da die Zeichnung jetzt sehr schnell geht, ist das auch möglich, wenn gleich ein kurzes Flackern auftritt.
In der (bald) folgenden Variante 12 ist es dann drin.
Beste Grüße
Mathematiker


Horst_H - So 16.09.12 11:30

Hallo,

diese ständigen Revisionen...
Ich habe mal meine uplasma.pas angehängt, und hoffe mal, dass sie auch unter Delphi kompiliert.
Ich habe die Bildausgabe aus der Zeitbestimmmung rausgeschmissen, dies war zu dominant.

Meine Version ist so ein wenig schneller.
Am besten mit Faktor = 0 und zebbow statt firestm

Gruß Horst


Mathematiker - So 16.09.12 14:18

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
diese ständigen Revisionen...

Entschuldigung, aber ich denke es ist besser, wenn die aktuelle Variante zum Download bereitsteht.
Nach der Revision 12 wird es aber wohl weniger werden, da ich vermute, dass nur noch eine völlig neue Idee eine deutliche Beschleunigung bewirkt. Und die haben wir im Moment noch nicht.
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe mal meine uplasma.pas angehängt, und hoffe mal, dass sie auch unter Delphi kompiliert.

Kein Problem und vielen Dank. Ich habe jetzt in das Programm wahlweise Deine und meine Routine eingebaut. Damit kann jeder entscheiden, welche er nutzt.
Auf meinem Rechner ist es so, dass Dein Vorschlag nahezu bei jeder Fenstergröße nur so viele Punkte berechnet, wie notwendig. Meine Lösung ist da noch schlechter.
Allerdings, und das verwundert mich, ist meine Fensterroutine für fast quadratische Abbildungen etwas schneller.

Beste Grüße
Mathematiker


Delphi-Laie - So 16.09.12 15:28

Ich wollte Dich bzw. dieses Dein Programm schon wieder über den grünen Klee loben, doch bei der manuellen Größenänderung (Resize, indem man am Formularrand "anfaßt") kommt vermutlich der Fehler, den ich schon einmal in dieser Diskussion bemängelte (Anhang). Ja, es kommt ggf. sogar eine "Fehlermeldungsausgabetreppe" nach rechts unten, die man von Windows nur allzu leidlich kennt.


Mathematiker - So 16.09.12 16:49

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Ich wollte Dich bzw. dieses Dein Programm schon wieder über den grünen Klee loben, doch bei der manuellen Größenänderung (Resize, indem man am Formularrand "anfaßt") kommt vermutlich der Fehler, den ich schon einmal in dieser Diskussion bemängelte (Anhang).

Nochmals Entschuldigung! Dieser Fehler sieht nach Problemen beim Laden der Farbpalette palette.c00 aus. Ich habe jetzt das neue Laden der Farben beim Zeichnen eines Bildes ausgegliedert.

Alternativ werde ich prüfen, ob ich die Farben nicht komplett in die Resource einbinden kann. Dann muss palette.c00 nicht mehr vorhanden sein und wahrscheinlich geht es auch schneller.
Sobald ich eine endgültige Lösung habe, melde ich mich noch einmal hier und füge die neue Revision ein.

Beste Grüße
Mathematiker


Delphi-Laie - So 16.09.12 17:10

Dann bin wohl eher ich derjenige, der um Entschuldigung bitten muß.

Meine Programmprüfung sah jedesmal einfach so aus, im Entpackprogrammfenster auf die Exe zu klicken und dann mit dieser zu werkeln, womit die Palette natürlich nicht verfügbar ist. Mit ihr scheint es aber zu klappen. Ich sah diese Palettendatei durchaus auch, konnte sie aber nicht zuordnen (daß es sich um irgendeine externe Datendatei handelt, ahnte ich aber bereits). Mühsamer zu programmieren, aber aussagekräftiger wäre eine Fehlerausgabe, die die fehlende Datei moniert.


Mathematiker - So 16.09.12 17:26

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Dann bin wohl eher ich derjenige, der um Entschuldigung bitten muß.

Musst Du nicht, ich hätte auch die Farben in die Resource packen und somit die Fehlerquelle verhindern können, was ich jetzt getan habe.

Ich wollte zwar nicht so schnell Rev 13 herausgeben, aber es bietet sich an. Jetzt müsste die Exe auch aus dem Zip-File heraus gestartet werden können.
Kleiner Nebeneffekt: Kein Extraladen von der Festplatte, der Quelltext wird "eleganter" und man braucht keine 2.Datei.
Auf diese Idee hätte ich schon lange kommen können. :roll:

Beste Grüße
Mathematiker


Horst_H - So 16.09.12 17:30

Hallo,

das war mir zuvor auch passiert.
Das habe ich in meiner uplama auch geändert, aber nicht zur Anzeige gebracht.

Was ich noch anmerken möchte:
bei der Zeitmessung wird auch die Zeit für das Zeichnen gestoppt.
Lazarus macht das ewig langsam und bei einer Paintbox von bxh = 1907x947 dauert die Rechnerei mit Lazarus 0,172 Sekunden, aber die Ausgabe über eine Sekunde.

Delphi-Quelltext
1:
2:
3:
      paintbox1.canvas.copyrect(zrect,bitmap.canvas,zrect);

      QueryPerformanceCounter(Time2);

Meine Version rechnet scheinbar nur richtig von dy > dx ist.
Unter Lazarus werden auch die Daten in anderen Registern / oder Reihenfolge übergeben, denn er springt bei Faktor= 0 nicht zu

Delphi-Quelltext
1:
2:
3:
4:
5:
assembler:
       cmp ecx, 1
       jbe @ohnea         //abweich < 2
...
       @ohnea:


Gruß Horst


Mathematiker - So 16.09.12 17:43

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Was ich noch anmerken möchte: bei der Zeitmessung wird auch die Zeit für das Zeichnen gestoppt.

Ich habe jetzt die Zeitmessung sofort nach der Berechnung durchgeführt und das Ergebnis ist richtig schön. Die Berechnung allein braucht unter Delphi weniger als 0,04 s.
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Unter Lazarus werden auch die Daten in anderen Registern / oder Reihenfolge übergeben

Als Notlösung schlage ich als erste Zeilen

Delphi-Quelltext
1:
2:
3:
4:
5:
    asm
         mov eax, f1
         mov edx, f2
         mov ecx, abweich
...

vor. Dann müsste es eigentlich funktionieren. Oder wir nutzen wieder die Delphi-Variante:

Delphi-Quelltext
1:
farbmitte:=max(1,((f1+f2) div 2 + random(abweich) - abweich div 2mod 256)                    

So groß war der Geschwindigkeitsgewinn durch Assembler auch nicht.
Ich werde Alles für Rev 14 berücksichtigen, die ich aber nicht gleich veröffentliche. :wink:

Beste Grüße
Mathematiker


Delphi-Laie - So 16.09.12 18:47

Das wird ja immer besser.

Generell ist es m.E. sinnvoll, Daten / Funktionen usw. dann permanent einzubinden, wenn sie (nahezu) immer benötigt werden und tendenziell nicht allzu groß sind. Je "optionaler" sie sind und je größer ihre Datenmenge ist, desto eher ist eine Externalisierung (z.B. in DLL(s), aber eben auch reinen Datendateien) gerechtfertigt. Aber auch, ob sie von nur einem Programm benötigt werden oder als Ressource für mehrer dienen, ist ziemlich ausschlaggebend, denn Redundanz ist häßlich.

Beim Spielen mit dem Faktor (scheint eine integre Variable zu sein, wenn man einen negativen Wert eingibt, sieht es aus wie bei einem sehr großen positiven) probierte ich auch noch einen gebrochenen, also eine Fließkommazahl. Wird nach Druck auf "Darstellung" mit einer anscheinend nicht individuell programmierten Fehlermeldung moniert. Solcherart ungültige Eingaben kann man, sofern man akribisch, penibel, pedantisch ist (das sind doch die meisten von uns Programmierern) mit einem try abfangen.

Warum Dein Programm so sehr die Aufmerksamkeit vieler auf sich zieht und Du üppige Unterstützung genießt, während ich sogar bei meinem Flaggschiff überwiegend auf mich allein gestellt war, ist mir ein wenig rätselhaft und macht mich sogar im nachhinein noch ein wenig neidisch.

Ergänzung: Allmählich fange ich wohl an, Dein Programm zu "quälen". Jedenfalls vertragen sich Zoomen des Formulares nicht mit der Farbrotation, d.h., wenn man bei laufender Rotation zoomt, würgt man damit die Rotation ab.


Mathematiker - So 16.09.12 19:00

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Beim Spielen mit dem Faktor (scheint eine integre Variable zu sein, wenn man einen negativen Wert eingibt, sieht es aus wie bei einem sehr großen positiven) probierte ich auch noch einen gebrochenen, also eine Fließkommazahl. Wird nach Druck auf "Darstellung" mit einer anscheinend nicht individuell programmierten Fehlermeldung moniert. Solcherart ungültige Eingaben kann man, sofern man akribisch, penibel, pedantisch ist (das sind doch die meisten von uns Programmierern) mit einem try abfangen.

Danke für den Hinweis, habe ich gleich geändert und außerdem noch negative Werte abgefangen.
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Warum Dein Programm so sehr die Aufmerksamkeit vieler auf sich zieht und Du üppige Unterstützung genießt, während ich sogar bei meinem Flaggschiff überwiegend auf mich allein gestellt war, ist mir ein wenig rätselhaft und macht mich sogar im nachhinein noch ein wenig neidisch.

Verstehe ich auch nicht, dennoch finde ich es schön. :) Vielleicht liegt es daran, dass die entstehenden Bilder merkwürdig und gleichzeitig schön sind.

Beste Grüße
Mathematiker


Horst_H - So 16.09.12 20:37

Hallo,

ich habe mal eine Variante mit getestet

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
//xm im Bereich 4/16..12/16 des Intervalles
          xm:=xa+((xe-xa)*(random(9)+4)) shr 4;
          ym:=ya+((ye-ya)*(random(9)+4)) shr 4;
{xm immer in der Mitte
          xm:=(xa+xe) shr 1;
          ym:=(ya+ye) shr 1;}

Die Rechnerei dauert dadurch ewig, da die Rekursionstiefe bis zu doppelt so groß werden kann.
das hat aber auch einen besonderen Effekt, den man Faktor= 0 besonders gut sieht.
Normal:
Zwischenablage02

und flexibel:
Zwischenablage01

Zebbow und haedache eignen sich besonders, da benachbarte Farben extrem unterschiedlich sind.

Gruß Horst


Mathematiker - So 16.09.12 21:39

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Hallo, ich habe mal eine Variante mit getestet ...
Die Rechnerei dauert dadurch ewig, da die Rekursionstiefe bis zu doppelt so groß werden kann.
das hat aber auch einen besonderen Effekt, den man Faktor= 0 besonders gut sieht.

Das sind interessante Gebilde, die dabei entstehen. Damit eröffnen sich viele Möglichkeiten, denn die Lage des Mittelpunktes (xm, ym) ist ja noch weiter veränderbar.
Zu testen wäre auch, wie sich das auf andere Faktoren > 0 auswirkt.
Mal sehen, ob ich noch weitere Farbpaletten zusammenstellen kann, vielleicht eine mit abwechselnd Weiß/Schwarz ...

Beste Grüße
Mathematiker


OlafSt - So 16.09.12 22:48

Oha, hier gehts wirklich Schlag auf Schlag ;)

Bezüglich des CMOVNZ habe ich bis D7 herunter getestet, der das klaglos übersetzt. Ich hatte nicht erwartet, das noch ältere Compiler hier unterwegs sind ;) Man könnte das CMOVNZ mit DB-Direktiven als Binärcode mit integrieren, aber ich glaube, wir lassen zugunsten der Lesbarkeit besser ;)

Was die Unterläufe angeht: Negative Werte werden einfach mit dem höchstwertigen Bit=1 dargestellt. -1 in EAX sieht also aus wie MAXINT, ist nur eine Frage der Interpretation. Da am Ende ohnehin ein AND EAX,$000000FF gemacht wird, aus der -1 ($FFFFFFFF) also eine 255 wird, spielt das keine ernsthafte Rolle und das ganze funktioniert einwandfrei.


Mathematiker - So 16.09.12 23:24

Hallo OlafSt,
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Ich hatte nicht erwartet, das noch ältere Compiler hier unterwegs sind ;)

Mein geliebtes Delphi 5 hat mir immer treue Dienste geleistet und da ich als Hobby-Programmierer nicht die Absicht habe, Datenbank- oder Netzanwendungen zu erstellen, werde ich es auch noch lange, lange, lange behalten. Solange, bis Microsoft dafür sorgt, dass 32 Bit-Anwendungen nicht mehr lauffähig sind. :lol:
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Man könnte das CMOVNZ mit DB-Direktiven als Binärcode mit integrieren, aber ich glaube, wir lassen zugunsten der Lesbarkeit besser ;)

Es klingt kompliziert und interessant!
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Was die Unterläufe angeht: Negative Werte werden einfach mit dem höchstwertigen Bit=1 dargestellt. -1 in EAX sieht also aus wie MAXINT, ist nur eine Frage der Interpretation. Da am Ende ohnehin ein AND EAX,$000000FF gemacht wird, aus der -1 ($FFFFFFFF) also eine 255 wird, spielt das keine ernsthafte Rolle und das ganze funktioniert einwandfrei.

Danke für die Erklärung, wieder etwas gelernt. Ich muss bei meiner Routine dennoch mit dem bedingten Sprung jz bei negativen Werten reagieren, da diese dann 1 werden müssen.

Hallo Delphi-Laie
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Allmählich fange ich wohl an, Dein Programm zu "quälen". Jedenfalls vertragen sich Zoomen des Formulares nicht mit der Farbrotation, d.h., wenn man bei laufender Rotation zoomt, würgt man damit die Rotation ab.

Das ist kein Quälen. :) Es ist sogar gut. Damit kann ich alle Fehler entfernen. Und das habe ich auch bei diesem Problem gerade getan. Langsam wird es wohl Zeit für die Revision 14. :wink:

Beste Grüße
Mathematiker


OlafSt - Mo 17.09.12 00:35

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:

Mein geliebtes Delphi 5 hat mir immer treue Dienste geleistet und da ich als Hobby-Programmierer nicht die Absicht habe, Datenbank- oder Netzanwendungen zu erstellen, werde ich es auch noch lange, lange, lange behalten. Solange, bis Microsoft dafür sorgt, dass 32 Bit-Anwendungen nicht mehr lauffähig sind. :lol:


No offense. Wenn man mit Assembler arbeitet, muß man solche Dinge berücksichtigen, denn natürlich wachsen die Delphi-internen Assembler mit den CPUs mit. DXE2 kann schließlich auch SSE4.2, was D2010 noch unbekannt ist, von 64Bit mal ganz zu schweigen.

Zitat:
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Man könnte das CMOVNZ mit DB-Direktiven als Binärcode mit integrieren, aber ich glaube, wir lassen zugunsten der Lesbarkeit besser ;)


Es klingt kompliziert und interessant!


Ich habe nochmal genauer nachgeschlagen (die Befehlsreferenz von Intel ist >200MB als PDF, und das ist nur Menmonics A-N...) und CMOVxx ist untauglich für unsere Zwecke. CMOVxx kann nur mit Registern arbeiten, nicht mit direkten Werten. Wir müßten also erst eine 1 in ein Register packen und dann CMOVen:

Quelltext
1:
2:
mov ebx,1
cmovnz eax,ebx


Dann ist aber unsere Optimierung für die Katz und wir können das ganze auch so lassen. Ich sehe da auch auf den zweiten Blick keine ernste Möglichkeit, noch Luft aus der Laufzeit herauszulassen :D

Gnaa, ich wollte mich mit meinem eigenen C#-Problem befassen, und nu kleb ich wieder hier dran :D:D:D


Tranx - Mo 17.09.12 07:27

Also ich muss schon sagen: Alle Achtung. Von mehr als 1 Sekunde (je nach Häufigkeit des Bildaufbaus) auf 0,016 s.

Das nennt man Optimierung!


Horst_H - Mo 17.09.12 08:26

Hallo,

etwas Offtopic:
wenn man das Mov EBX,1 weiter vorne erledigen kann, verschwindet dessen Laufzeit, weil es parallel auf der einzelnen CPU verarbeitet werden kann.
Irgendwie bekommen die ( Intel/AMD etc) es ja hin, 3 oder mehr Befehle parallel zu bearbeiten IPC = 3
Siehe mal mein alten AMD http://www.delphipraxis.net/1150448-post40.html 31 Befehle in 14 Takten oder hier 2,56 Befehle / Takt
http://www.entwickler-ecke.de/viewtopic.php?t=91942&postorder=asc&highlight=buchstaben+z%E4hlen&start=37
Die neueren Intel-CPU's haben AMD schon weit abgehängt.
Ein Thread/CPU: Amd bei um die 1 und Intel rauf bis 1,65.Das sind gewaltige Unterschiede.
http://pcpartpicker.com/benchmarks/cinebench/render-single/overall-list/

@Tranx:
Zu den Sekunden sollte man CPU-Takte oder zumindest die CPU-Taktrate angeben.
Also 0.01478 s für 597*513 = 306261 Bildpunkte auf einem AMD X4 955 mit 3,2 Ghz getestet mit Lazarus1.0.1/FPC 2.6.0. ergibt 154,4 Takte pro Bildpunkt.
Delphi ist meist schneller.
Ein Bild mit 584x584 Bildpunkte braucht 156,3 Takte, obwohl dann keine Berechnung zu viel, während bei 597x513 Bildpunkten fast 30% mehr gerechnet wird.
Das ist ja das Fatale/die Crux, man rechnet mehr, spart sich aber Vergleiche, die bei falscher Vorhersage 20 Takte kosten ( auch hier ist Intel auf dem Weg zu 10 Takten )und damit mehr Zeit kosten als die eigentliche Berechnung.

Gruß Horst
P.S
Die Berechnung selbst ist gar nicht so viel schneller geworden, nur das Zeichnen wurde verlegt und statt die Bitmap selbst zu bearbeiten ein Feld bearbeitet.


Delphi-Laie - Mo 17.09.12 10:43

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Ich hatte nicht erwartet, das noch ältere Compiler hier unterwegs sind ;)


Warum nicht ("erwartet" bzw. "hier unterwegs sind")?

Meine Profil ist fast schon mein (nicht nur Delphi-)Programm, allerdings haben bei mir auch Turbodelphi und XE2 ihren Stammplatz, diese aber nur bei Bedarf. Von XE3 muß ich mir erst mal eine Testversion besorgen, um auf dem neuesten Stand zu sein. XE2 in der ersten offiziellen Version war wirklich schlimm (eher noch Beta als Gamma). Etwas derart unausgegorenes erlebte ich weder bei den Delphi-/C++-IDEs noch überhaupt bei kommerziellen Programmen....

Traurig finde ich nur, daß man mit Delphi nicht für niedrige Versionen speichern kann (wie es viele Programme können), so daß ich Mathematikers Delphi-5-Datendateien (konkret natürlich die Formulardateien) nicht mit Delphi 4 lesen kann. Und Delphi 5 mir deshalb extra zu installieren habe ich keine Lust.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Allmählich fange ich wohl an, Dein Programm zu "quälen". Jedenfalls vertragen sich Zoomen des Formulares nicht mit der Farbrotation, d.h., wenn man bei laufender Rotation zoomt, würgt man damit die Rotation ab.

Das ist kein Quälen. :) Es ist sogar gut. Damit kann ich alle Fehler entfernen. Und das habe ich auch bei diesem Problem gerade getan.


Mathematiker, das hat seinen Grund: In den 90er Jahren stellte ich jemandem ein selbstgeschriebenes Programm vor. Der erzählte mir von jemand anderem, der, wenn man ihm ein Programm vorstellte, als erstes folgendes machte: Er drückte mehrere Tasten der Tastatur, ja, er legte beide Hände darauf ab, um möglichst viele Tasten gleichzeitig zu treffen, und konnte meistens, nämlich dann, wenn das Programm daraufhin abstürzte, genüßlich resümieren: "Und was ist das?"

In der Tat konnte man DOS-Programmen, die nur oberflächliche Standardeingaberoutinen verpaßt bekamen, so den Garaus machen, unter Windows dürfte das hingegen kein Problem mehr sein. Seitdem bin ich bezüglich (eben auch fehlerhafter) Nutzereingaben sensibilisiert und ein Enthusiast ergonomischer (wie ich sie verstehe und empfinde) Programme. So muß ein Programm immer und überall, d.h. zu jeder Zeit und in jeder Sitation, auf Nutzereingaben vorbereitet sein, eben auch auf die falschen. Konsequenterweise "öffnet" man ein Programm nur in den Situationen, in denen diese gewünscht sind, für bestimmte Eingaben.


OlafSt - Mo 17.09.12 12:00

IMHO gehört diese "Idiotensicherheit" zum guten Ton, wenn man professionell programmiert. Ich habs auch so gelernt und lege meine Programme darauf aus (sogar die TAB-Reihenfolge und ALT-Hotkeys ;) ). Ich weiß, ist inzwischen völlig aus der Mode gekommen, so zu arbeiten. Bis man sein Programm mal an den richtigen gibt :D


Mathematiker - Mo 17.09.12 12:02

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Traurig finde ich nur, daß man mit Delphi nicht für niedrige Versionen speichern kann (wie es viele Programme können), so daß ich Mathematikers Delphi-5-Datendateien (konkret natürlich die Formulardateien) nicht mit Delphi 4 lesen kann.

Warum hast Du denn noch nichts gesagt? :bawling:
Wenn ich gewusst hätte, dass es unterschiedliche Formate gibt, hätte ich schon lange reagiert. Unter Delphi 5 muss ich nur mit einem Rechtsklick auf das Formular zwischen Text- und Binary-Format umschalten. Das habe ich jetzt getan.
Die Revision 14 müsste damit Dein Delphi 4 lesen. Hoffe ich.

Und noch einmal. Ich bin in keiner Weise beleidigt, wenn jemand einen Fehler in irgendeinem meiner Programme findet. Im Gegenteil. Das zeigt mir doch, dass das Programm benutzt wird.
Außerdem gibt es mir die Möglichkeit, die Fehler zu entfernen und vor allem aus diesen zu lernen.
Deshalb kann ich nur alle bitten, fleißig zu testen und mir Probleme mitzuteilen.

Beste Grüße
Mathematiker


Delphi-Laie - Mo 17.09.12 14:20

"Lässig", damit ist es lesbar, danke.

Das trifft aber für alle Deine Projektquelltexte und auch so ziemlich für alle Projektquelltexte anderer Forumsteilnehmer zu, die mit neueren Delphis erstellt wurde. Anscheinend wurde es es mit Delphi 5 möglich, die Formular-/VCL-Informationen als Text abzuspeichern, und daß das umschaltbar ist und dann sogar von Delphi 4 gelesen werden kann, erstaunt mich sehr. Zur Not kann man ja auch mit Rechtsklick auf Formular eine Formulardatei komplett als Quelltext mit VCL-Elementen versorgen.

Sooo schlimm war es nun aber auch wieder nicht, denn das Geheimnis steckt ja fast ausschließlich in den eigentlichen Quelltexten.

Na gut, wenn Du Dich nicht auf den Schlips getreten fühlst, gleich noch eins: Das Neuzeichnen funktioniert auch noch nicht sauber. Schiebt oder zoomt man ein anderes Fenster "stufig" über die Graphik hinweg, und zwar so, daß nur ein Teil neugezeichnet wird (werden muß), und danach ist wieder ein Stückchen mehr der Graphik zu zeichnen, ist diese nicht mehr homogen.


Mathematiker - Mo 17.09.12 18:03

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Schiebt oder zoomt man ein anderes Fenster "stufig" über die Graphik hinweg, und zwar so, daß nur ein Teil neugezeichnet wird (werden muß), und danach ist wieder ein Stückchen mehr der Graphik zu zeichnen, ist diese nicht mehr homogen.

Ich vermute Du nutzt Windows XP, denn unter Vista und 7 gibt's das eigentlich nicht mehr.
Aber auch das Problem glaube ich gelöst zu haben. Allerdings muss ich morgen erst einmal auf einem XP-Computer prüfen, ob der Fehler noch auftritt. Wenn nicht, nun ja, dann wird es vielleicht Revision 15 sein.
Ich hätte nie gedacht, dass es so viele geänderte Programmversionen werden. :wink:

Beste Grüße
Mathematiker


Delphi-Laie - Mo 17.09.12 18:26

2000.

Sollte m.E sogar auf jedem Windows fehlerfrei funktionieren, auf dem es lauffähig ist. Bin in der Beziehung akribisch. Nur, wenn der Aufwand gar zu groß wäre, kann man natürlich die alten Betriebsprogramme ignorieren. Muß eben jeder für sich entscheiden. XP erfreut sich aber immer noch großer Beliebheit und Verbreitung.


Mathematiker - Mo 17.09.12 23:00

Hallo Delphi-Laie,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Nur, wenn der Aufwand gar zu groß wäre, kann man natürlich die alten Betriebsprogramme ignorieren.

Warum sollte ich? Es war keine große Mühe und ich hoffe, dass es jetzt funktioniert, d.h. unter Windows XP und 2000 das Bild sauber aufgebaut wird.

Eine weitere Ablaufbremse habe ich die ganze Zeit übersehen, das round beim Einrechnen des Faktors. Dabei genügt ein

Quelltext
1:
shr ecx, 1                    

am Anfang der Prozedur farbmitte und man kann eine integer-Variable faktor, statt real, nutzen und zwei mal round sparen. Außerdem ist die Variable anzahl überflüssig und auch shr 1 ist etwas günstiger als div 2.
Da die Bilderzeugung mittlerweile schon ziemlich schnell ist, sind 6 ms Zeitgewinn je Bild (Anfangsgröße) auf meinem PC scheinbar nicht viel. Es sind aber immerhin knapp 10 %.
Übrigens kostet das Zählen der Berechnungen auch noch 1 ms. Dennoch muss es erst einmal enthalten bleiben, bis eine Lösung gefunden ist, die mitunter überflüssigen Berechnungen auf Null zu bringen.

Ich werde jetzt unverschämt und setze das (unrealistische?) Ziel, weniger als 50 ms auf meinem langsamen Rechner zu erreichen. Im Moment sind es 56 ms. Nebenbei würde mich interessieren, ob die Berechnung und Darstellung eigentlich mit C# schneller wäre. Ich habe davon keine Ahnung.
Da es doch wieder ein deutliche Änderung ist, bedeutet das Revision 15. Ich verspreche keine weitere Revision 16, sagen wir, vor Mittwoch. :lol:

Beste Grüße
Mathematiker


Horst_H - Di 18.09.12 08:43

Hallo,

ein paar kleine Bemerkungen:
Wie ich weiter oben erwähnte sind, kann die Logik, um unnötige Berechnungen aus zu schließen, mehr Zeit kosten, als die Berechnung selbst.
Eine Bitmap ist eine Null Basiertes Feld also von 0..Höhe-1/0..Breite-1.
Also muss das farbFeld entsprechend groß sein und nicht 1 breiter und höher.
Das geht auch wenn man die Startwerte richtig setzt.

Delphi-Quelltext
1:
2:
3:
4:
5:
      //Startwerte
      farbfeld[0,0]:=random(255)+1;
      farbfeld[hoehe-1,0]:=random(255)+1;
      farbfeld[0,breite-1]:=random(255)+1;
      farbfeld[hoehe-1,breite-1]:=random(255)+1;


Mich hat schon "früher" gestört, das bei der crt -Routine GotoXY ( Spalte,Zeile) war, aber im Bildschirmspeicher (MemW[B8000:Zeile*Spaltenbreite+Spalte] es genau andersherum ist.Eben passend zur zeilenweise Ausgabe des Bildes auf dem Fersehbildschirm=CRT mit dem Elektronenstrahl .
Bitmaps sind auch so gespeichert, vielleicht erkennbar an scanline.
Das GotoXY hast Du unwissentlich beibehalten bei Farbfeld.
Das bedeutet aber , dass Punkte, die horizontal nebeneinanderliegen, im Speicher immer um Breite versetzt sind und bei Verwendung eines zweidimensionalen dynamischen Feldes auch sonstwo liegen können, aber mindestens um Breite+Konstant versetzt.
Dadurch wird die Zuweisung des FarbFeld-> Bitmap gebremst, weil die Daten nicht schön nebeneinander mit maximaler Geschwindigkeit aus dem Cache gelesen werden, sondern ständig aus dem Hauptspeicher oder Level2/3 Cache nachgeladen werden müssen.Deshalb ist das auf dem Notebook bei mir wesentlich langsamer.

Lange Rede keinen Sinn:
Ich würde alle farbFeld Änderung auf Hoehe/Breite umstellen.

Delphi-Quelltext
1:
setlength(farbfeld,paintbox1.height,paintbox1.Width);                    

und dazu den Aufruf von Fenster ändern, y bleibt ja Höhe.
procedure fenster(ya,ye,xa,xe:integer);
Innerhalb von fenster müssen dann alle x und y bei Farbfeld getauscht werden.Z.B.:

Delphi-Quelltext
1:
2:
3:
4:
farbe1:=farbmitte(pixel1,pixel3,dy);
             farbfeld[xa,ym]:=farbe1;
in
             farbfeld[ym,xa]:=farbe1;

Ob das wesentlich schneller ist kann ich jetzt nicht testen, da ich noch mit Lazarus hadere, um scanline hin zu bekommen.Dort kann man auch direkt auf die BitMap zugreifen mit Bitmap.RawImage.Data;

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
procedure TForm1.InsBildKopieren(Sender: TObject);
var
  i,j,index:integer;
  pfF,pPix:pByte;
begin

{$IFDEF FPC}
  pPix:= Bitmap.RawImage.Data;
  for i:=0 to paintbox1.height-1 do
    begin
    pfF := @farbfeld[i,0];
    for j:=0 to paintbox1.width-1 do
      begin
      index := pfF^;
      IF index <> 0 then
        begin
        index := (index + cyclestart);
        IF Index > 255 then
          dec(Index,254);
        end;
      inc(pfF);
      with pal[index] do
        begin
        pPix^:= r;inc(pPix);
        pPix^:= g;inc(pPix);
        pPix^:= b;inc(pPix);
        // bitmap.canvas.pixels[i,j]:=rgb(r,b,g);
        end;
      end;
    end;

{$ELSE}

Irgendwie schreibt das Programm nur Daten, wenn die Form verändert wird, sehr kurios.Mal schauen, wo ich den Fehler mache.

Meine Version von Fenster, kannst Du getrost entfernen, die ist nicht schneller.

Hier ein Link, der etwas wesentlich beschleunigen könnte:
http://www.pjh2.de/delphi/articles/graphic/bitmap.php
wenn man die Bitmap als 8Bit- Paletten Bitmap anlegt, braucht man nur deren Palette ändern/ um eine Position verschieben und nicht jeden einzelnen Punkt.( Bei FullHd fast 2 Mio )
Siehe dort
http://www.pjh2.de/delphi/articles/graphic/bitmap.php#Methode1

Ich versuche das mit der Palette mal das umzusetzen.

Mein Versuch mittels Getmem(pFarbFeld,Breite*Hoehe) mir dynamische 2-dimensionale Feld zu sparen, ist kläglich gescheitert.
In Freepascal, bekomme ich eine Adresse in pFarbfeld und es funktioniert und mit Lazarus zeigt pFarbfeld auf $1 und das sofort einen Zugriffsfehler.
Wahrscheinlich hat die Unit Windows ( nur wegen queryperformancecounter ) auch ein getmem. Ärgerlicher Kram das...

Gruß Horst


OlafSt - Di 18.09.12 13:37

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Nebenbei würde mich interessieren, ob die Berechnung und Darstellung eigentlich mit C# schneller wäre. Ich habe davon keine Ahnung.


Auf keinen Fall. C# wird in einen Zwischencode übersetzt (IL genannt). Anschließend wird dieser IL-Code vom JIT-Compiler interpretiert und ausgeführt. Zweifellos ist der JITC, den Microsoft da gebaut hat, schnell wie die Sünde, ändert aber nichts daran.

Ergo: Es kann, genauso wie Java, unmöglich so schnell sein wie direkter Binärcode.


Horst_H - Di 18.09.12 14:05

Hallo,

bezüglich C#.
Bei softgames/developia gab in einem Forum mal eine Diskussion, 4 gewinnt möglichst schnell auf Gewinn zu testen.
Es lief dann darauf hinaus, das 6x7 Feld als als 3 Int64 ( einmal Rot/gelb und Vereinigungsmenge) aufzufassen und einfach alle Möglichenkeiten mittels einer Maske zu testen.
Mit Java dauerte ein Test im Schnitt 10 Takte. In der Art
For i := 0 to 52 do
IF SpielfeldRot AND Maske[i]= Maske[i] ...

Das fand ich sehr schnell, weiß aber nicht mehr, ob es auf einem 64-Bit Betriebssystem lief

Gruß Horst


Delphi-Laie - Di 18.09.12 14:42

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Nebenbei würde mich interessieren, ob die Berechnung und Darstellung eigentlich mit C# schneller wäre. Ich habe davon keine Ahnung.


Auf keinen Fall. C# wird in einen Zwischencode übersetzt (IL genannt). Anschließend wird dieser IL-Code vom JIT-Compiler interpretiert und ausgeführt. Zweifellos ist der JITC, den Microsoft da gebaut hat, schnell wie die Sünde


Ist das jetzt Ironie? Ich vermute, eher nicht.

Ich weiß nicht genau, ob dier IL-Code schon bei der Programmerstellung oder erst beim Programmstart erzeugt wird, nach meiner Kenntnis ersteres. Wird das Programm gestartet, muß natürlich erst einmal dieser Zwischencode compiliert werden. Klar, das dauert ein wenig, aber danach liegt doch ein vollwertiges Compilat vor, an dem man dann nach Herzenslust die Sündengeschwindigkeit genießen kann.


Mathematiker - Di 18.09.12 19:24

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Ich würde alle farbFeld Änderung auf Hoehe/Breite umstellen.

Wie immer eine tolle Idee. Obwohl ich skeptisch war, habe ich es ausprobiert und es ist tatsächlich wieder 2 ms schneller.
Du hast zwar ausführlich erklärt, warum es schneller sein muss, ich verstehe es auch, bin aber doch verwundert.
Für die Zukunft heißt das also auch bei anderen Projekten genau zu überlegen, welche Koordinate usw. bei einem zweidimensionalen Feld zuerst steht. Irgendwie verrückt. :eyecrazy:

Deinen zweiten Hinweis mit der Palette habe ich schon einmal gesehen. Ich werde es ausprobieren. Vielleicht ergibt sich damit ein Vorteil bei der abchließenden Erzeugung des Bildes.
Bezugnehmend auf Deinen Hinweis bei den 2 Matherätseln: Ja es sind einige "Baustellen". Meine Erfahrung ist aber, dass man das eine oder andere Problem, z.B. p196, erst einmal einige Zeit ruhen lässt, bis man wieder eine neue Idee hat. :zwinker:
Beim Plasma nähern wir uns ja auch dem Optimum, d.h. bald ist es eine Baustelle weniger.

Beste Grüße
Mathematiker


Mathematiker - Di 18.09.12 21:45

Hallo an alle Mitstreiter,

:beer: Geschafft! Weniger als 50 ms je Bild! :beer:

Durch die kleine Änderung am Ende der Fenster-Methode

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
        if uy then begin
          farbe5:=(farbe1+farbe2+farbe3+farbe4) shr 2;
          farbfeld[ym,xm]:=farbe5;
          //rekursive Konstruktion
          fenster(xa,xm,ya,ym);
          fenster(xm,xe,ya,ym);
        end;
        fenster(xa,xm,ym,ye);
        fenster(xm,xe,ym,ye);

und das Entfernen der Berechnungszählung bekomme ich jetzt Zeiten von etwas mehr als 48 ms bei der Startfenstergröße. Die Berechnung allein erfolgt in etwa 29 ms, der Rest wird für die Bitmapkonstruktion gebraucht. Zu Beginn dieses Programms waren es insgesamt etwa 8400 ms für ein Bild!
Außerdem werden bei einem Bild mit Breite>Höhe keine Punktberechnungen mehr zu viel durchgeführt; für Höhe>Breite ein paar, die aber kaum bremsen. Untersucht man unterschiedliche Bildgrößen zeigt sich, dass die Berechnungszeit proportional zur Pixelzahl wächst, wie es auch sein soll.
Das Ergebnis stelle ich als Revision 16 ein, obwohl ich keine neue Variante vor Mittwoch versprochen hatte. :P

Ich danke allen, die hier fleißig mitgeholfen haben. :wave:
Zwar soll man nie sagen, dass es nicht mehr besser geht, dennoch glaube ich, wir haben vorerst das Optimum erreicht.
Eure Ideen sind weiterhin sehr willkommen. Vielleicht geht's ja noch schneller: 40 ms, 30 ms, 20 ms, 10 ms, ...

Beste Grüße
Mathematiker


Horst_H - Mi 19.09.12 09:32

Hallo,

endlich habe ich in Lazarus/FPC den Scanline Ersatz gefunden, die auch wirklich neuzeichnet.
BeginUpdate und EndUpdate war die Lösung.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
procedure TForm1.InsBildKopieren;
var
  i,j,index:integer;
  pfF,pPix:pByte;
  rowrgb : pbytearray;
  BitmapRawData :ptrUint;
  delta : ptrUint;//DWord;
begin
  Bitmap.BeginUpdate();
  // Abstand Zeile n bis Zeile n+1 bei pf24Bit= 3 Byte aufgerundet auf 32Bit-Teilbarkeit
  // So sind Bitmap definiert.
  {$IFDEF FPC}
  delta := ((Bitmap.Width*3-1)DIV 4+1)*4;
  BitmapRawData:= ptrUint(Bitmap.RawImage.Data);
  {$ENDIF}
  for j:=0 to bitmap.height-1 do
    begin
    {$IFDEF FPC}
    pPix := pbyte(BitmapRawData+j*delta);
    {$ELSE}
    pPix:= pbyte(bitmap.scanline[j]);
    {$ENDIF}
    pfF := @FarbFeld[j,0];
    for i:=0 to bitmap.width-1 do
      begin
      index := pfF^;

      IF index <> 0 then
        begin
        index := (index + cyclestart);
        IF Index > 255 then
          dec(Index,254);
        end;
      inc(pfF);
      with pal[index] do
        begin
        pPix^:= r;inc(pPix);
        pPix^:= g;inc(pPix);
        pPix^:= b;inc(pPix);
        end;
      end;
    end;
  Bitmap.EndUpdate();
  paintbox1.canvas.draw(0,0,bitmap);
end;

Diese Konstrukt sieht etwas seltsam aus, aber in früheren Versionen sollte 0 immer 0 bleiben sollte und die anderen Werte sich von 1..255 ändern können.
Aber 0 kommt bei der Erzeugung garnicht vor, Farbmite ist ja immer >= 1.
Das kostet also wenig Zeit, da die Sprungvorhersage funktioniert.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
      index := pfF^;

      IF index <> 0 then
        begin
        index := (index + cyclestart);
        IF Index > 255 then
          dec(Index,254);
        end;


Eine kleine Abwandlung:
Ich berechne den Rand zuerst und spare ein paar IF/ Sprünge, ein falscher Sprung kostet mich ja 20 Takte.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
    {$ELSE}
    //Mittelwert der Farben berechnen
    function farbmitte(f1,f2,abweich: integer): integer;
    begin
      result := (f1+f2) shr 1;
      IF abweich > 0 then
        result:=max(1,(result + random(abweich) - abweich shr 1AND 255)
    end;
    {$ENDIF}

    procedure fensterX0(ya,ye:integer);
    var
      ym,dy:integer;
    begin
      IF ye-ya > 1 then
        begin
        dy := (ye-ya)*faktor;
        ym := (ya+ye) shr 1;
        farbfeld[ym,0] :=farbmitte(farbfeld[ye,0],farbfeld[ya,0],0);
        fensterX0(ya,ym);
        fensterX0(ym,ye);
        end;
    end;

    procedure fensterY0(xa,xe:integer);
    var
      xm,dx:integer;
    begin
      IF xe-xa > 1 then
        begin
        dx := (xe-xa)*Faktor;
        xm := (xa+xe) shr 1;
        farbfeld[0,xm] :=farbmitte(farbfeld[0,xe],farbfeld[0,xa],0);
        fensterY0(xa,xm);
        fensterY0(xm,xe);
        end;
    end;


    procedure fenster(xa,xe,ya,ye:integer);
    var
        xm,ym,dx,dy:integer;
        sum:word;
        ux,uy:boolean;
        pixel2,pixel3,pixel4,farbe:byte;

    begin
        if (xe-xa<2and (ye-ya<2then
           EXIT;

        //Diamond-Square-Algorithmus
        xm:=(xa+xe) shr 1;
        ym:=(ya+ye) shr 1;

        pixel2:=farbfeld[ya,xe];
        pixel3:=farbfeld[ye,xa];
        pixel4:=farbfeld[ye,xe];

        dy:=(ye-ya)*faktor;
        dx:=(xe-xa)*faktor;
        if waagerecht then ux:=xm<xe
                      else ux:=xm>xa;
        uy:=ym>ya;

        sum :=farbfeld[ya,xm]+farbfeld[ym,xa];

        if uy then begin
          farbe:=farbmitte(pixel2,pixel4,dy);
          farbfeld[ym,xe]:=farbe;
          inc(sum,farbe);
        end;

        if ux then begin
          farbe:=farbmitte(pixel3,pixel4,dx);
          farbfeld[ye,xm]:=farbe;
          inc(sum,farbe);
        end else exit;

        if uy then begin
          farbfeld[ym,xm]:=sum shr 2;;

          fenster(xa,xm,ya,ym);
          fenster(xm,xe,ya,ym);
        end;

        fenster(xa,xm,ym,ye);
        fenster(xm,xe,ym,ye);
     end;

Ich brauche mit Lazarus auf meinem Rechner jetzt 84,5 ms für 1,808e6 Bildpunkte.
Sind immer noch 150 Takte/pro Pixel.Wow , ganze 6 Takte schneller ;-)
Das Kopieren in die Bitmap und anschliessende Ausgabe kosten 32 ms.Das liegt aber auch daran, das meine Grafikkarte; wie wohl bei den meisten, auf pf32Bit eingestellt ist.
Man kann seinen Rechner auch ärgern ;-) , indem man alle 3 Byte was abholt statt 1,2,4,8,16-byte mäßig.
Mein Vorschlag: pal wieder in vfarben umwandeln und dann mit pf32bit arbeiten.
Dann wird pPix ein Zeiger auf ein integer und vFarben in eins kopiert.

Mal schauen, was das bringt.

Gruß Horst
EDIT1:
Das war ja ein Reinfall.Nichts, niente, nada .
paintbox1.canvas.draw(0,0,bitmap); braucht alleine 17 der 32 ms, auch bei pf32bit.


Mathematiker - Mi 19.09.12 17:15

Hallo Horst_H,
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Ich berechne den Rand zuerst und spare ein paar IF/ Sprünge, ein falscher Sprung kostet mich ja 20 Takte.

Die Idee ist nicht schlecht. Ich habe das ausprobiert und es bringt bei mir etwa eine halbe Millisekunde.

Übrigens habe ich heute einmal, probeweise, Lazarus installiert. Sieht gut aus!
Allerdings habe ich den Eindruck, dass es noch komplizierter ist als mein Delphi 5, und vor allem das Compilieren dauert ziemlich lange. Interessant ist es aber. Im Moment habe ich noch keine Möglichkeit gefunden, ein Delphi-Projekt zu laden, das finde ich aber noch.

Beste Grüße
Mathematiker


Horst_H - Mi 19.09.12 18:14

Hallo,

Projekt -> schliessen und dann erscheint schon in der Auswahl, Delphi-Projekt umwandeln.
Ja, Lazarus ist beim starten/komplieren sehr lahm, da werden zig units durchgeorgelt..
Keine Sorge, das Kompilat ist meist langsamer, aber hoffentlich sind die Zeiten vorbei, wo die äussere von drei Schleifen ein Register belegt und die Daten ständig aus dem Speicher/in den Speicher und so weiter geschoben werden, weil die Register knapp sind.Da hilft es enorm, die innere Schleife in eine extra Prozedur zu packen.

Gruß Horst


Delphi-Laie - Mi 19.09.12 21:58

user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Projekt -> schliessen und dann erscheint schon in der Auswahl, Delphi-Projekt umwandeln.


Eine mögliche und bequeme, aber leider in einer Beziehung ziemlich schlechte Variante, weil es die berüchtigten Riesencompilate erzeugt.

Besser ist ein compilatsgrößenminimiertes (mit den entsprechenden Einstellungen) Projekt zu laden und diesem die benötigten Projektinformationen "manuell" beizubringen. Oder man kennt sich wirklich mit allen relevanten Projekteinstellungen aus (ich gehöre nicht zu diesem elitären Kreise):


Mathematiker - Mi 19.09.12 22:37

Hallo Horst_H,
ich muss noch einmal auf
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Ich berechne den Rand zuerst und spare ein paar IF/ Sprünge, ein falscher Sprung kostet mich ja 20 Takte.

zurückkommen.
Ich habe jetzt jeweils 1000 Bilder, einmal mit besonderer Berechnung der Randpunkte und einmal ohne, erzeugen lassen. Ich war etwas vorschnell, mit der Aussage, dass die externe Berechnung des linken und oberen Randes beschleunigt, zumindest nicht unter meinem Delphi 5 auf meinem PC.
Deine neue Idee finde ich immer noch überzeugend, aber aus einem mir noch nicht ganz klaren Grund, wird es sogar etwas, nicht viel, langsamer. Ich vermute, dass die Erhöhung der Zahl der rekursiven Aufrufe bremst. Bisher wurden zwar für jeden Nichtrandpunkt zwei überflüssige Tests durchgeführt, aber insgesamt weniger oft die fenster-Routine aufgerufen.
Irgendwie finde ich es trotzdem seltsam.

Beste Grüße
Mathematiker

PS: Meine ersten Versuche mit Lazarus sind nicht so gut gelaufen, wie erhofft. Vor allem "nervt" mich die lange Compilierzeit. Da der erste Eindruck angeblich der beste ist, bin ich ziemlich frustriert.


Delphi-Laie - Mi 19.09.12 22:57

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
PS: Meine ersten Versuche mit Lazarus sind nicht so gut gelaufen, wie erhofft. Vor allem "nervt" mich die lange Compilierzeit. Da der erste Eindruck angeblich der beste ist, bin ich ziemlich frustriert.


Dann bist Du also ein ähnlicher Umweltfreund wie ich und benutzt einen älteren Computer, anstatt Dir den neuesten und schnellsten zu kaufen. Ich lasse Lazarus compilieren und tue während der Zeit etwas anderes (z.B. in der Entwickerecke zu surfen ;-) ). Ist fast wie in der Computerfrühzeit.

Mit der Compilatsgröße läßt sich die Compilierzeit auch marginal beeinflussen, weil kleinere Compilate nun einmal schneller geschrieben werden (können). Vieles weitere kann man bei Lazarus einstellen, jedoch kann man, wenn man sich alles mögliche beim Compilieren anzeigen läßt, die Compilierzeit bis fast unendlich steigern, bei Abschalten der wichtigen Dinge, was nicht ratsam ist, wird es natürlich marginal schneller. Auch sonst muß man sich an einiges gewöhnen. Weil der Compiler aber empfindlicher gegenüber Quelltextunsauberkeiten ist (Delphi ist in der Beziehung toleranter), zwingt er auch zu größerer Sorgfalt. Letztlich ergänzen sich beide (Delphi und Lazarus/FPC), und es kann generell nicht schaden, den Quelltext für den einen dem jeweils anderen vorzusetzen (sich sozusagen eine zweite Expertenmeinung einzuholen).


Mathematiker - So 28.08.16 17:09

Hallo,
bei den ersten Versuchen mit Delphi 10.1 habe ich auch die Plasmadarstellung testen wollen. Unter D5 und D7 läuft alles problemlos, bei Delphi 10.1 kommt aber eine Exception
fehler
in der Assemblerroutine

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
 //Mittelwert der Farben berechnen
    function farbmitte(f1,f2,abweich: integer): integer;
    asm
         shr ecx, 1
         add eax, edx       //(f1+f2) div 2
         shr eax, 1
         cmp ecx, 1
         jbe @ohnea         //abweich < 2
         push eax           //(f1+f2) div 2 speichern
         xor edx, edx       //Zufallsgenerator
         mov eax, ecx       //Initialisierung auf 0 bis abweich
         imul edx, [randseed], $08088405
         inc edx
         mov [randseed], edx
         mul edx
         mov eax, edx       //in eax steht zufallswert [0,abweich-1]
         pop edx            //(f1+f2) div 2 zurückholen
         add eax, edx       //Addition (f1+f2) div 2
         shr ecx, 1         //abweich div 2
         sub eax, ecx       //subtraktion abweich div 2
         jc @null
         @ohnea: and eax, 255       //mod 256
         cmp eax, 0         //Test auf Null
         jz @null
         ret               //HIER KOMMT DIE EKCEPTION            
         @null: mov eax, 1
       end;

Da ich nur rudimentäre Kenntnisse von ASM habe, bin ich etwas ratlos.
Sieht jemand den Fehler?

Danke für jeden Hinweis
Mathematiker


Delphi-Laie - So 28.08.16 17:41

In bezug auf Assembler bin ich ein mindestens ebensolcher Analphabet wie Du, vermutlich noch heftiger.

Aber zwei Fragen fallen mir spontan ein:

1. 32- oder 64-Bit-Compilat?
2. Schon mit Debuggen versucht?


Mathematiker - So 28.08.16 17:46

Hallo,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
1. 32- oder 64-Bit-Compilat?

Die Delphi 10.1 Starter-Version produziert nur 32bit-Programme.
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
2. Schon mit Debuggen versucht?

Genau das habe ich gemacht. Deshalb habe ich ja das RET als Fehlerstelle gefunden.

Beste Grüße
Mathematiker


Delphi-Laie - So 28.08.16 17:59

Ich kann da auch nur vermuten und täte an Deiner Stelle z.B. probieren: Muß das "ret ..." nicht in eine Zeile geschrieben werden? Er mosert das ret an einer Stelle an, an der ich an des Computers Stelle auch nicht wüßte, wohon ich returnieren sollte....


Mathematiker - So 28.08.16 19:50

Hallo,
ich habe das Ende der ASM-Routine in

Delphi-Quelltext
1:
2:
3:
4:
5:
         cmp eax, 0         //Test auf Null
         jz @null
         jmp @exit
         @null: mov eax, 1
         @exit:
geändert und schon kommt keine Fehlermeldung mehr. Warum es jetzt geht? Ich weiß es nicht.
Allerdings macht jetzt scanline Ärger. Scheinbar ist dieses Projekt nicht so einfach auf Delphi 10.1 zu transformieren.

Beste Grüße
Mathematiker

Nachtrag: Ich habe es hinbekommen. Im Anhang die Exe und der Quelltext, der unter 10.1 funktioniert.


Delphi-Laie - So 28.08.16 21:19

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Nachtrag: Ich habe es hinbekommen. Im Anhang die Exe und der Quelltext, der unter 10.1 funktioniert.


An der genannten Assemblerfunktion scheint es nicht zu liegen, denn deren letzte Zeilen sind immer noch so, wie von Dir veröffentlicht.

Welcher Spürsinn ließ Dich denn womit erfolgreich sein?


Mathematiker - So 28.08.16 21:40

Hallo,
user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
An der genannten Assemblerfunktion scheint es nicht zu liegen, denn deren letzte Zeilen sind immer noch so, wie von Dir veröffentlicht.

Nicht ganz. Das Return habe ich herausgeworfen und springe nun an das Ende der Assemblerroutine.
Ich weiß nicht, warum ich es probiert habe, aber ich hatte irgendwie das "Gefühl", dass ein RET mitten in der Befehlsfolge nicht richtig ist.
Kurz gesagt: Versuch und Irrtum.

Beste Grüße
Mathematiker


Delphi-Laie - So 28.08.16 21:50

Ach so, ich verglich es mit Deiner letzten Veröffentlichung und konnte keine Abweichung entdecken. Evtl. hast Du mit dem Edit nicht nur den Nachtrag veröffentlicht, sondern auch den Quelltext entsprechend angepaßt.

Das nackte "ret" in einer Zeile gefiel mir ja von Anfang an nicht.

So, wie Du es anscheinend tatest, mit Versuchen und Irrtümern, programmiere auch ich oft genug...

Ergänzung: Die Exe-Dateigröße ist wirklich abenteuerlich. Anscheinend und leider wurde das Problem der wachsenden Compilatsgrößen leider nie ernsthaft angegangen.


mandras - So 28.08.16 23:37

Guten abend :)

Ich hab grad kein neueres Delphi zur Hand, für mich sieht es aber wie folgt aus:

Die Funktion ist ohne begin/end definiert.
Neuere Delphis entscheiden dann ob sie die als echte Funktion oder als inline-Code erzeugen.
Wenn es inline-code ist kann / darf! darin kein RET stehen, da der Code der Funktion
ja nicht mittels CALL aufgerufen wurde.