| Autor |
Beitrag |
ultra2k
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 01:56
HalliHallo, lang nix mehr geschrieben hier, aber zur zeit habe ich wieder ein Problemchen...
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:
| procedure TForm1.BlendFrame(var bmp: TBitmap); type PixArray = Array [0..2] of Byte; var i, j: Integer; p: ^PixArray; begin bmp.PixelFormat := pf24Bit;
i := FocusFrame.Top; repeat if not ((i < 0) or (i > Screen.Height - 1)) then begin p := bmp.ScanLine[i]; Inc(p, FocusFrame.Left); j := FocusFrame.Left; repeat if not ((j < 0) or (j > Screen.Width - 1)) then begin p^[0] := Round(p^[0] - ((p^[0] - GetBValue(clHighlight)) / 100 * OverlayIntensity)); p^[1] := Round(p^[1] - ((p^[1] - GetGValue(clHighlight)) / 100 * OverlayIntensity)); p^[2] := Round(p^[2] - ((p^[2] - GetRValue(clHighlight)) / 100 * OverlayIntensity)); end; Inc(p); Inc(j); until j = FocusFrame.Right; end; Inc(i); until i = FocusFrame.Bottom; end; |
Also in dieser Prozedur soll einfach in einem Bitmap ein bestimmter Bereich (FocusFrame) eingefärbt werden, klappt soweit wunderbar (also bisher gabs keine Grafikfehler), den Code habe ich schon mehrfach überarbeitet und mittlerweile habe ich die Geschwindigkeit mehr als verdoppeln können, allerdings läuft das ganze für meinen Geschmack immer noch nicht schnell genug...
Der Effekt, den ich erreichen will ist einen Rahmen mit eingefärbtem inneren zu Zeichnen, ähnlich wie wenn man unter XP im Explorer dateien markiert (sofern angeschaltet). Dass die Herren von MS das ganze beschleunigt ablaufen lassen ist mir auch klar, allerdings bevor ich mich mit OpenGL/DirectX etc. befassen muss, hätte ich doch gern gewusst, ob ihr an dem Code noch ein paar Sachen findet, die zu optimieren wären
Vielen Dank im vorraus 
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
jaenicke
      
Beiträge: 19341
Erhaltene Danke: 1752
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 10.09.07 02:50
1. Nimm eine for statt einer repeat Schleife, du kennst ja Start- und Zielwert. Damit fällt der Vergleich bei jedem Durchlauf der beiden Schleifen weg. 2. Berechne GetBValue(clHighlight), etc. sowie OverlayIntensity / 100 vor den Schleifen und pack das in Variablen, damit fallen bei jeder inneren Schleife 3 Funktionsaufrufe und 1 Rechenoperation weg.
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 02:54
Okay stimmt... und auf sowas komm ich nich  *baum*
Aber das mit der For-Schleife? Man hat mir mal gesagt, dass Repeat- und While-Schleifen schneller sind als For-Schleifen...
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
jaenicke
      
Beiträge: 19341
Erhaltene Danke: 1752
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 10.09.07 03:01
Das mit der for-Schleife kannst du ja mit QueryPerformanceCounter ausmessen. Ich hätte in diesem Fall die for-Schleife für schneller gehalten, muss aber zugeben, dass ich mir da nicht so sicher bin. Ich sollte mir vielleicht mal ansehen, was der Compiler aus dem Code macht. Welcher der Assembler-Codes schneller ist, sehe ich ja dann...
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 03:06
Also es is ja schon spät und ich bin auch hundemüde, aber so sollte das doch eigentlich richtig sein, oder?
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:
| procedure TnMagWND.BlendFrame(var bmp: TBitmap); type PixArray = Array [0..2] of Byte; var i, j, a: Integer; p: ^PixArray; r, g, b: Byte; begin bmp.PixelFormat := pf24Bit;
b := GetBValue(clHighlight); g := GetGValue(clHighlight); r := GetRValue(clHighlight); a := 100 * OverlayIntensity;
i := FocusFrame.Top; repeat if not ((i < 0) or (i > Screen.Height - 1)) then begin p := bmp.ScanLine[i]; Inc(p, FocusFrame.Left); j := FocusFrame.Left; repeat if not ((j < 0) or (j > Screen.Width - 1)) then begin p^[0] := Round(p^[0] - ((p^[0] - b) / a)); p^[1] := Round(p^[1] - ((p^[1] - g) / a)); p^[2] := Round(p^[2] - ((p^[2] - r) / a)); end; Inc(p); Inc(j); until j = FocusFrame.Right; end; Inc(i); until i = FocusFrame.Bottom; end; |
...tut er aber nicht 
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
jaenicke
      
Beiträge: 19341
Erhaltene Danke: 1752
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 10.09.07 03:09
Hihi, Rechenregeln... Ich habe nicht umsonst OverlayIntensity / 100 und nicht 100 * OverlayIntensity für den Inhalt der Variablen geschrieben...
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 03:14
Achso... oh mann -.- ja gut ich geh wohl jetzt besser mal schlafen
Geändert hab ichs, bringt aber geschwindigkeitstechnisch keinen signifikanten Unterschied würde ich mal behaupten, jedenfalls bemerke ich keinen 
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
jaenicke
      
Beiträge: 19341
Erhaltene Danke: 1752
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 10.09.07 03:44
Ok, du hast Recht, repeat ist schneller (ca. 1,5%).
Die Variante mit repeat und Zwischenspeicherung ist um ca. 3% schneller als ohne Zwischenspeicherung in Variablen.
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Mo 10.09.07 11:07
Ich würde das so hier machen, ist ca 16x schneller nach meiner Messung:
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:
| uses Math;
procedure TForm1.BlendFrame(var bmp: TBitmap); type PixArray = Array [0..2] of Byte; var y, x: Integer; p: ^PixArray; Dest: TRect; r, g, b: Byte; LUTr, LUTg, LUTb: array[0..255] of Byte; begin bmp.PixelFormat := pf24Bit;
b := GetBValue(ColorToRGB(clHighlight)); g := GetGValue(ColorToRGB(clHighlight)); r := GetRValue(ColorToRGB(clHighlight));
for x := 0 to 255 do begin LUTr[x] := x - Round((x-r) / 100 * OverlayIntensity); LUTg[x] := x - Round((x-g) / 100 * OverlayIntensity); LUTb[x] := x - Round((x-b) / 100 * OverlayIntensity); end;
Dest.Left:=Max(FocusFrame.Left, 0); Dest.Top:=Max(FocusFrame.Top, 0); Dest.Right:=Min(FocusFrame.Right, Screen.Width-1); Dest.Bottom:=Min(FocusFrame.Bottom, Screen.Height-1);
for y:=Dest.Top to Dest.Bottom do begin p := bmp.ScanLine[y];; Inc(p, Dest.Left); for x:=Dest.Left to Dest.Right do begin p^[0] := LUTb[p^[0]]; p^[1] := LUTg[p^[1]]; p^[2] := LUTr[p^[2]]; Inc(p); end; end; end; |
Ansonsten würde ich nicht pf24bit sondern pf32bit verwenden, müsste auch noch etwas schneller sein ;O)
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 10.09.07 12:26
Phantom1: Evtl wäre es da noch ganz praktisch die LookupTabellen außerhalb zu erzeugen und diese dann zu übergeben. Damit müsste man es nicht ständig neu berechnen.
Bzw Scanline hat durchaus auch einen kleinen Overhead. Wenn es sehr zeitkritisch ist kann das durchauch auch 1-2 ms ausmachen. Aber sonst zum reinen Blenden mit einer Farbe denke ich geht es wohl nicht mehr schneller.
ultra2k: Generell solltest du auf die Berechnung mit Fließkommawerten bei Bildern verzichten. Denn das extrem häufige Mischung von FPU und CPU mag dein Rechner ganz und gar nicht. Als kleines Beispiel mal ein paar Messungen aus einem kleinem Program von mir. Denke das sagt mehr als alles andere. Zwei 800x600 Bilder werden dort mittels eines Faktors geblendet. Die Zeiten beinhalten nur das reine Blenden. Ich mache noch ein paar Sachen außen rum aber die sind überall gleich deswegen dauert das Darstellen auch etwas länger.
FaqF1 und FaqF2 sind auf 1 normiert und Faq1 und Faq2 auf 255.
Trunc: 85ms 1:
| Red := Trunc((Red_1 * FaqF1) + (Red_2 * FaqF2)); |
Round: 75ms 1:
| Red := Round((Red_1 * FaqF1) + (Red_2 * FaqF2)); |
Div: 41ms 1:
| Red := ((Red_1 * Faq1) + (Red_2 * Faq2)) div $FF; |
Shr: 11ms 1:
| Red := ((Red_1 * Faq1) + (Red_2 * Faq2)) shr 8; |
Für die ganz Wahnsinnigen. Mit MMX geht das Ganze in 2.7ms
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 13:24
Wow, vielen Dank erst mal für die rege Beteiligung und eure Codeschnipsel und Messungen  *supi*
@Lossy: Deine Shr-Methode scheint ja am flinkesten zu sein, nur verstehen tu ich sie bislang noch nicht. Red ist ja vermutlich ein Byte, wie bei mir, dann wird ja gerechnet und "shr" heißt ja "shift right", wenn ich nicht irre, aber dann habe ich doch überall Nuller drin stehen oder nich
Und was: Shift right ist dann richtung least significant bit oder lieg ich da falsch?
Also nochmal vielen Dank und ich werd mich gleich nochmal dransetzen, sobald ich meinen Kram hier erledigt hab 
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 10.09.07 13:56
Also wenn es wirklich nur eine Farbe ist, dann solltest du eher das von Phantom1 benutzen und die Berechnung der LookupArrays auslagern. Also in Form.Create oder so. Schneller als das wird es nicht gehen, denn dort wird nämlich gar nicht mehr gerechnet.
Und zu dem Shift Right. Das lohnt sich nur, wenn du 2 Bilder blenden möchtest. Und das sieht dann wie folgt aus. Ich habe ja 2 Faktoren. Der Eine gibt an wie sichtbar das eine Pixel ist und der andere wie Sichtbar das zweite Pixel ist. Normiert auf 255. Faq1 + Faq2 ergeben zusammen 255. Also bei 60% Sichtbarkeit ist Faq1 = 153 und Faq2 = 102. Wenn du die jetzt verrechnest und addierst ergibt sich maximal ein 255 * 255. Also ein 16 Bit Wert und da die CPU intern eh mit 32 Bit Registern arbeitet geht auch nichts an den Daten verlohren. Das Ganze muss dann noch durch 255 gerechnet werden und fertig. Und das Ergebniss von div 255 ist das Gleiche wie wenn es um 8 Bits nach rechts verschoben wird. Nur etwas schneller, da keine Arithmetischen Operationen benutzt werden.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| var Faq1, Faq2: Byte; begin Faq1 := $FF * OverlayIntensity div 100; Faq2 := $FF - Faq1;
for y := 0 to height do for y := 0 to height do pDest^[0] := ((pSrc1^[0] * Faq1) + (pSrc2^[0] * Faq2)) shr 8; pDest^[1] := ((pSrc1^[1] * Faq1) + (pSrc2^[1] * Faq2)) shr 8; pDest^[2] := ((pSrc1^[2] * Faq1) + (pSrc2^[2] * Faq2)) shr 8; end; end; |
Aber wie oben schon mal gesagt. Wenn es nur mit einer festen Farbe blenden möchtest, dann nimm die Methode wie Phantom1 sie beschrieben hat. Das ist schneller als es jedes Mal zu berechnen. Ich hatte das nur gesagt für den Fall, dass du später auch mal andere Sachen machen willst und da sollte man Floats nur benutzen, wenn es nicht anders geht.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 19:01
Also ich hab das jetzt mal eingebaut und es läuft wirklich super, so hab ich mir das vorgestellt!
@Lossy: Danke für die Erklärung, jetzt hab ichs geschnallt, ich hab ja schon oft Programme geschrieben die viele Bitoperationen besaßen, allerdings nur in c/c++ und die meißten davon für den PowerPC, aber das ist auch schon wieder eine Weile her und man vergisst dann doch das eine oder andere
@Phantom1: Danke für die Prozedur, die ist ja wie gesagt wirklich sehr flink. Ich staune auch immer wieder wie trickreich und clever die Informatiker doch oft sind. Ich wollte das ganze noch umbauen in zwei Repeat-Schleifen weil die ja noch nen Tick schneller sind, da hab ich dann noch irgendwo nen fehler reingebaut und das Programm schlussendlich zum Stehen gebracht  das krieg ich aber wohl selbst hin denk ich, da hab ich sicher in der eile nur was übersehen
EDIT: Wusst ichs doch, die Art von Fehler, von der man besser gar nicht erst spricht ^^
EDIT2: Achso ganz vergessen, wenn ich mit 32Bit arbeite, MUSS ich dann den Alpha-Kanal jedes mal auf 255 setzen? Weil meine Bitmaps haben von haus aus keine Transparenz und ohne gehts auch bei mir (also "darf" man das?)
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
Zuletzt bearbeitet von ultra2k am Mo 10.09.07 19:27, insgesamt 1-mal bearbeitet
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Mo 10.09.07 19:26
ultra2k hat folgendes geschrieben: |
@Phantom1: Ich wollte das ganze noch umbauen in zwei Repeat-Schleifen weil die ja noch nen Tick schneller sind, da hab ich dann noch irgendwo nen fehler reingebaut und das Programm schlussendlich zum Stehen gebracht |
Wie kommst du darauf? While und Repeat Schleifen sind wesentlich langsamer als For-Schleifen, da bei den While/Repeat schleifen bei jedem durchgang eine if-bedingung ausgeführt wird.
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mo 10.09.07 19:30
Ja und bei ner For-Schleife nicht? Irgendwo muss der doch gucken, ob er am ziel angekommen ist, und ne Repeat-Schleife in Assembler besteht aus nem Compare und einem Jump if not Equal (plus dem Increase), und ich kann mir nicht vorstellen, dass eine For-Schleife aus noch weniger abfragen besteht...
Ausserdem:
jaenicke hat folgendes geschrieben: | Ok, du hast Recht, repeat ist schneller (ca. 1,5%).
Die Variante mit repeat und Zwischenspeicherung ist um ca. 3% schneller als ohne Zwischenspeicherung in Variablen. |
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Mo 10.09.07 20:04
Ich habs es warscheinlich nur falsch erklärt, auf jeden fall ist eine for-schleife schneller als eine while/repeat schleife.
Habs eben extra nochmal gegengetestet:
Folgender Code lief bei mir mit 87 ms:
Delphi-Quelltext 1: 2: 3:
| for i := 0 to 100000000 do begin a:=a+1; end; |
Und dieser hier mit 136 ms:
Delphi-Quelltext 1: 2: 3:
| repeat a:=a+1; until a=100000000; |
Der Unterschied ist doch recht groß ;O)
EDIT: wegen dem 32bit format, das ist nicht immer sinnvoll. Macht nur sinn wenn das Bild nicht bei jedem aufruf in 32bit konvertiert werden muss.
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Di 11.09.07 13:39
Jo das mit den 32Bit kann ich auch woanders hinstecken, damit es nicht bei jeden Aufruf ausgeführt wird, allerdings habe ich festgestellt, dass pf32Bit an einer anderen Stelle an der ich per BitBlt auf einen Canvas kopiere wesentlich langsamer ist als pfDevice, was ja default ist, daher der Aufruf...
Das mit den Schleifen werde ich mir auch nochmal ansehen, aber ich kann es irgendwie trotzdem (noch) nicht glauben, ich muss mir mal anschauen was Delphi für nen Maschinencode draus macht.
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Di 11.09.07 14:38
Bei den Schleifen kann es passieren, dass Delphi sie rumdreht und damit auf 0 überprüft. Und das ist schneller und einfacher als auf einen Wert zu überprüfen. Und bei 100 Mio Aufrufe machen selbst die kleinsten Veränderungen etwas aus.
pfDevice muss nicht zwingend schneller sein als pf32Bit. Allerdings erzwingt pf32Bit eine Ausführung in einem geräteunabhängigen Format wärend pfDevice ein geräteabhängiges Format ist. Mit anderen Worten pfDevice wird unter Umständen von der Grafikkarte beschleunigt. Allerdings kannst du bei pfDevice nicht mehr auf die Scanlines zugreifen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
ultra2k 
      
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Di 11.09.07 15:07
Lossy eX hat folgendes geschrieben: | | pfDevice muss nicht zwingend schneller sein als pf32Bit. Allerdings erzwingt pf32Bit eine Ausführung in einem geräteunabhängigen Format wärend pfDevice ein geräteabhängiges Format ist. Mit anderen Worten pfDevice wird unter Umständen von der Grafikkarte beschleunigt. Allerdings kannst du bei pfDevice nicht mehr auf die Scanlines zugreifen. |
Jo das weiß ich, allerdings brauch ich das ausserhalb meiner Blend-Funktion auch nicht, von daher wechsel ich immer zwischen den beiden Formaten und das klappt ja sehr gut
Lossy eX hat folgendes geschrieben: | | Bei den Schleifen kann es passieren, dass Delphi sie rumdreht und damit auf 0 überprüft. Und das ist schneller und einfacher als auf einen Wert zu überprüfen. Und bei 100 Mio Aufrufe machen selbst die kleinsten Veränderungen etwas aus. |
Jo das wollte ich auch gerade schreiben, ich hab mich nämlich eben hingesetzt und ein wenig rumprobiert und mir mal das Assembler-Ergebnis von delphi näher angeguckt (siehe Anhang). Es sieht wohl so aus, als würde Delphi die Schleife umdrehen, wenn man nicht mehr auf die Laufvariable zugreift
Andernfalls gleicht das Schleifenkonstrukt genau dem der Repeat-Schleife, gut zu wissen...
Wie hast du die Zeit gemessen bei deinem Vergleich? Sowas hab ich vorher noch nicht gemacht, mein Code sah so aus und lieferte witzigerweise für alle Schleifen (For/Repeat/While) ähnliche Ergebnisse, deshalb hatte ich mir auch den Assemblercode mal vorgenommen.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TMainForm.ButtonRepeatClick(Sender: TObject); var start, stop: Int64; a: Integer; begin a := 0; QueryPerformanceCounter(start); repeat a := a + 1; until a = 100000000; QueryPerformanceCounter(stop); LabelRepeat.Caption := IntToStr(stop-start); end; |
Kann man das überhaupt so machen, oder kommt da mist raus?
Einloggen, um Attachments anzusehen!
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Di 11.09.07 16:30
Gemessen habe ich das so hier:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TForm1.Button1Click(Sender: TObject); var start, stop, freq: Int64; a: Integer; begin a := 0; QueryPerformanceFrequency(freq); QueryPerformanceCounter(start); repeat a := a + 1; until a = 100000000; QueryPerformanceCounter(stop); Label1.Caption := IntToStr(Round((stop-start)*1000/freq)); end; |
Man sollte aber immer mehrere Tests durchführen und einen durchschnittswert nehmen. Es sollten auch keine Hintergrundprogramme laufen die cpu-lastig sind. Ansonsten wäre es auch noch sinnvoll die Anwendung in RealTime Priorität laufen zu lassen.
|
|
|