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,
Delphi-Laie hat folgendes geschrieben : |
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.
Delphi-Laie hat folgendes geschrieben : |
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
Mathematiker hat folgendes geschrieben : |
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.
Mathematiker hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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.
Delphi-Laie hat folgendes geschrieben : |
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 begin paintbox1.canvas.draw(0, 0, 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
hathor 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; begin [...] end; |
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(0, 0, 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.
Tranx hat folgendes geschrieben : |
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.
Tranx hat folgendes geschrieben : |
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:
| 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,
mandras hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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.
Delphi-Laie hat folgendes geschrieben : |
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.
Delphi-Laie hat folgendes geschrieben : |
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); 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,
Tranx hat folgendes geschrieben : |
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); 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.
Tranx hat folgendes geschrieben : |
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,
Horst_H hat folgendes geschrieben : |
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 2) mod 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 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
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 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,
Horst_H hat folgendes geschrieben : |
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
Horst_H hat folgendes geschrieben : |
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,
Tranx hat folgendes geschrieben : |
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
Mathematiker hat folgendes geschrieben : |
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 |
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,
Gammatester hat folgendes geschrieben : |
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.
Gammatester hat folgendes geschrieben : |
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 shr eax, 1 push eax xor eax, eax mov edx, ecx push eax xchg eax, edx sub eax, edx imul edx, [randseed], $08088405 inc edx mov [randseed], edx mul edx pop eax add eax, edx shr ecx, 1 sub eax, ecx pop edx add eax, edx and eax, 255 cmp eax, 0 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 2) mod 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,
Delphi-Laie hat folgendes geschrieben : |
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).
Delphi-Laie hat folgendes geschrieben : |
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
Mathematiker hat folgendes geschrieben : |
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 pop edx add eax, edx and eax, 255 cmp eax, 0 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 pop edx add eax, edx and eax,255 cmovnz eax,1 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);
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; 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 OR bDy) then exit;
inc(AnzahlBerechnungen); xm:=(xa+xe) div 2; ym:=(ya+ye) div 2; IF xa = 0 then inc(farbtupfer); IF ya = 0 then inc(farbtupfer);
IF bdy then inc(farbtupfer,1);
IF bdx then inc(farbtupfer);
IF bdx AND bdy then inc(farbtupfer);
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; 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; 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,
OlafSt hat folgendes geschrieben : |
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<=1) and (ye-ya<=1) then 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; asm add eax, edx shr eax, 1 cmp ecx, 1 jbe @ohnea push eax xor eax, eax mov edx, ecx push eax xchg eax, edx sub eax, edx imul edx, [randseed], $08088405 inc edx mov [randseed], edx mul edx pop eax add eax, edx pop edx add eax, edx shr ecx, 1 sub eax, ecx jc @null @ohnea: and eax, 255 cmp eax, 0 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; asm add eax, edx shr eax, 1 cmp ecx, 1 jbe @ohnea push eax xor edx, edx mov eax, ecx imul edx, [randseed], $08088405 inc edx mov [randseed], edx mul edx mov eax, edx pop edx add eax, edx shr ecx, 1 sub eax, ecx jc @null @ohnea: and eax, 255 cmp eax, 0 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
Delphi-Laie hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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,
Horst_H hat folgendes geschrieben : |
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.
Horst_H hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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 ... @ohnea: |
Gruß Horst
Mathematiker - So 16.09.12 17:43
Hallo Horst_H,
Horst_H hat folgendes geschrieben : |
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.
Horst_H hat folgendes geschrieben : |
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 2) mod 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,
Delphi-Laie hat folgendes geschrieben : |
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.
Delphi-Laie hat folgendes geschrieben : |
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:=xa+((xe-xa)*(random(9)+4)) shr 4; ym:=ya+((ye-ya)*(random(9)+4)) shr 4; |
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:
und flexibel:
Zebbow und haedache eignen sich besonders, da benachbarte Farben extrem unterschiedlich sind.
Gruß Horst
Mathematiker - So 16.09.12 21:39
Hallo Horst_H,
Horst_H hat folgendes geschrieben : |
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,
OlafSt hat folgendes geschrieben : |
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:
OlafSt hat folgendes geschrieben : |
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!
OlafSt hat folgendes geschrieben : |
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
Delphi-Laie hat folgendes geschrieben : |
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
Mathematiker hat folgendes geschrieben : |
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: |
OlafSt hat folgendes geschrieben : | 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
OlafSt hat folgendes geschrieben : |
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.
Mathematiker hat folgendes geschrieben : |
Delphi-Laie hat folgendes geschrieben : | 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,
Delphi-Laie hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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
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:
| 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); 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
Mathematiker hat folgendes geschrieben : |
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
OlafSt hat folgendes geschrieben : |
Mathematiker hat folgendes geschrieben : | 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,
Horst_H hat folgendes geschrieben : |
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; 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;begin Bitmap.BeginUpdate(); {$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} 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 1) AND 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<2) and (ye-ya<2) then EXIT;
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,
Horst_H hat folgendes geschrieben : |
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
Horst_H hat folgendes geschrieben : |
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
Horst_H hat folgendes geschrieben : |
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
Mathematiker hat folgendes geschrieben : |
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
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:
| function farbmitte(f1,f2,abweich: integer): integer; asm shr ecx, 1 add eax, edx shr eax, 1 cmp ecx, 1 jbe @ohnea push eax xor edx, edx mov eax, ecx imul edx, [randseed], $08088405 inc edx mov [randseed], edx mul edx mov eax, edx pop edx add eax, edx shr ecx, 1 sub eax, ecx jc @null @ohnea: and eax, 255 cmp eax, 0 jz @null ret @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,
Delphi-Laie hat folgendes geschrieben : |
1. 32- oder 64-Bit-Compilat? |
Die Delphi 10.1 Starter-Version produziert nur 32bit-Programme.
Delphi-Laie hat folgendes geschrieben : |
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 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
Mathematiker hat folgendes geschrieben : |
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,
Delphi-Laie hat folgendes geschrieben : |
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.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!