Autor |
Beitrag |
user32
      
Beiträge: 55
Erhaltene Danke: 5
|
Verfasst: Mo 09.05.11 02:52
Also, es geht hier um Optimierung einer Bitmap + Scanline Routine. Das Ganze hat weniger einen praktischen Zweck sondern ist eher zum Lernzweck für mich, da ich noch relativer Newbie bin in Assembler.
Also... Im Moment dauert es 5,8 ms um ein 1100x800 Bild mit gelb-rotem Rauschen zu füllen. Noch wichtiger: Die Funktion braucht knapp 10,7 Mio Takte (gemessen). Also 12 Takte/Pixel.. naja. Immerhin 40% schneller als nur mit Delphi. Aber das reicht mir noch nicht
Ich hab schon soweit ich konnte versucht zu optimieren, aber ich denke mal da geht noch was. Sind doch gradmal <1Mio Pixel
Vielleicht hat ja jemand ne Idee.
Evtl. irgendwelche Opcodes die weniger Takte brauchen...?
Thx!
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:
| p := _image.ScanLine[height-1]; asm push EBX push ECX push EDX
rdtsc mov DWORD PTR[time1+$4],EDX mov DWORD PTR[time1+$0],EAX mov EAX,width mov ECX,height imul ECX,EAX xor EAX,EAX mov EDX,[p] imul EBX,ECX,3
@@again: sub EBX,3
call RandX mov BYTE PTR[EDX+EBX+$00],$00 mov BYTE PTR[EDX+EBX+$01],AL mov BYTE PTR[EDX+EBX+$02],AH loop @@again
rdtsc mov DWORD PTR[time2+$4],EDX mov DWORD PTR[time2+$0],EAX
pop EDX pop ECX pop EBX end; |
|
|
Gammatester
      
Beiträge: 328
Erhaltene Danke: 101
|
Verfasst: Mo 09.05.11 11:27
user32 hat folgendes geschrieben : | Also... Im Moment dauert es 5,8 ms um ein 1100x800 Bild mit gelb-rotem Rauschen zu füllen. Noch wichtiger: Die Funktion braucht knapp 10,7 Mio Takte (gemessen). Also 12 Takte/Pixel.. naja. Immerhin 40% schneller als nur mit Delphi. Aber das reicht mir noch nicht
Ich hab schon soweit ich konnte versucht zu optimieren, aber ich denke mal da geht noch was. Sind doch gradmal <1Mio Pixel
Vielleicht hat ja jemand ne Idee.
|
Wenn man sich die Schleife ansieht, wird doch wohl die meiste Zeit mit call RandX verbraucht!? Wie sind denn die Werte, wenn diese Anweisung auskommentiert wird?
Selbst wenn es eigentlich wenig sinnvoll ist, Pseudo-Zufallsrauschen zu malen, kann man auf jedenfall Zyklen sparen, wenn der Code für RandX direkt in die Schleife eingebaut wird (selbst wenn RandX 100% optimiert ist - was auch immer das bedeutet). Ein zweiter Ansatz wäre Loop-Unrolling. Eventuell ist auch (je nach Prozessor) sub ecx,1 / jnz @@again schneller als loop @@again.
|
|
bummi
      
Beiträge: 1248
Erhaltene Danke: 187
XP - Server 2008R2
D2 - Delphi XE
|
Verfasst: Mo 09.05.11 12:59
hat zwar mit ASM nicht zu tun, aber wenn Du statt für jede Zeile Scanline abzufragen direkt über die letzte Scanline mit berectneten Offsets über Höhe und Breite zugreifst kannst nochmals ordentlich Performance rausholen, Anhang ist zwar Delphi, aber das Zugriffsprinzip beleibt das gleiche
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:
| Procedure FastPixel(BMP:TBitmap;LastScanLine:PCArray;x,y:Integer;Farbe:TColor); begin LastScanLine[(Bmp.Height - y) * Bmp.Width + x]:= Color2RGB(Farbe); end; procedure TForm2.Button4Click(Sender: TObject);
var x,y,w:Integer; tc:Cardinal; CArray,CArray2:PCArray; begin tc := GetTickCount; image1.Canvas.FillRect(image1.clientRect); image1.picture.Bitmap.PixelFormat := pf32bit; w := Image1.Picture.Bitmap.Width; CArray := Image1.Picture.Bitmap.ScanLine[Image1.Height - 1];
for y := 1 to image1.picture.Bitmap.Height -1 do begin for x := 0 to image1.picture.Bitmap.Width -1 do begin FastPixel(image1.picture.Bitmap,CArray,x,y,clBlue); end; end; Caption := IntToStr(GetTickCount-tc); end; |
_________________ Das Problem liegt üblicherweise zwischen den Ohren H₂♂
DRY DRY KISS
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 09.05.11 14:25
Moin!
Gammatester hat folgendes geschrieben : | Wenn man sich die Schleife ansieht, wird doch wohl die meiste Zeit mit call RandX verbraucht!? |
Man kann mit rückgekoppelten Schieberegistern Pseudozufallszahlen erzeugen, das sollte das schnellste Verfahren sein, wenn es nicht auf die Güte ankommt.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 09.05.11 15:18
|
|
user32 
      
Beiträge: 55
Erhaltene Danke: 5
|
Verfasst: Mo 09.05.11 15:29
Hi,
bummi hat folgendes geschrieben : | hat zwar mit ASM nicht zu tun, aber wenn Du statt für jede Zeile Scanline abzufragen direkt über die letzte Scanline mit berectneten Offsets über Höhe und Breite zugreifst kannst nochmals ordentlich Performance rausholen, Anhang ist zwar Delphi, aber das Zugriffsprinzip beleibt das gleiche
|
Ich greife doch nur einmal drauf zu! Oder meinst du Scanline VORHER abzufragen und in einer globalen Variable speichern?
Gammatester hat folgendes geschrieben : | Wenn man sich die Schleife ansieht, wird doch wohl die meiste Zeit mit call RandX verbraucht!? Wie sind denn die Werte, wenn diese Anweisung auskommentiert wird?
Selbst wenn es eigentlich wenig sinnvoll ist, Pseudo-Zufallsrauschen zu malen, kann man auf jedenfall Zyklen sparen, wenn der Code für RandX direkt in die Schleife eingebaut wird (selbst wenn RandX 100% optimiert ist - was auch immer das bedeutet). |
Das ist eine gute Idee! RandX wird noch von anderen Prozeduren gebraucht, deswegen hab ich garnicht dran gedacht sie hier in den Code einzugliedern! Also RandX sieht bis jetzt so aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| function RandX:DWORD; asm push EDX push ECX
mov EAX,RandSeed mov ECX,RandSeed imul EDX,ECX,87654321h mov RandSeed,EDX mul EDX mov EAX,EDX
pop ECX pop EDX end; |
Ich werd sie gleich mal in den Loop integrieren. Vielleicht kann ich ja die Pushs und Pops loswerden.
Gammatester hat folgendes geschrieben : |
Ein zweiter Ansatz wäre Loop-Unrolling. Eventuell ist auch (je nach Prozessor) sub ecx,1 / jnz @@again schneller als loop @@again. |
Jnz hab ich schon probiert. War ganze 10ms langsamer...warum auch immer.
Narses hat folgendes geschrieben : | Moin!
Gammatester hat folgendes geschrieben : | Wenn man sich die Schleife ansieht, wird doch wohl die meiste Zeit mit call RandX verbraucht!? | Man kann mit rückgekoppelten Schieberegistern Pseudozufallszahlen erzeugen, das sollte das schnellste Verfahren sein, wenn es nicht auf die Güte ankommt.
cu
Narses |
Ich werds mir gleich mal angucken!
Mfg
--- Moderiert von Narses: Beiträge zusammengefasst---
4,5 Millisek bzw. 7,1 Millionen clock cycles.
Hm ja, mit dem LFSR bin ich mir nicht sicher, ob ich es richtig gemacht habe. Sieht jedenfalls ziemlich zufällig aus.
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:
| p := _image.ScanLine[height-1]; asm push EBX push ECX push EDX push ESI push EDI
rdtsc mov DWORD PTR[time1+$4],EDX mov DWORD PTR[time1+$0],EAX
mov EAX,width mov ECX,height imul ECX,EAX mov EDX,[p] imul EBX,ECX,3 mov ESI,EDX
@@again: sub EBX,3 mov EAX,RandSeed mov EDI,$409 xor EAX,EDI rol EAX,1 INC EAX mov RandSeed,EAX mul EDI mov BYTE PTR[ESI+EBX+$00],$00 mov BYTE PTR[ESI+EBX+$01],AL mov BYTE PTR[ESI+EBX+$02],AH loop @@again
rdtsc mov DWORD PTR[time2+$4],EDX mov DWORD PTR[time2+$0],EAX
pop EDI pop ESI pop EDX pop ECX pop EBX end; |
|
|
|