[ASM] Bitmap + Scanline
spacer
Autor Nachricht
user32
Hält's aus hier
Beiträge: 2



BeitragVerfasst: 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! ;)

ausblenden volle Höhe Delphi-Quelltext markieren
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];   // der aufruf allein dauert schon 2ms :(
asm
push EBX
push ECX
push EDX

rdtsc // zum takte zählen
mov DWORD PTR[time1+$4],EDX // 64bit variable
mov DWORD PTR[time1+$0],EAX //

mov EAX,width
mov ECX,height
imul ECX,EAX // anzahl der pixel
xor EAX,EAX // (farbe: schwarz) nicht mehr
mov EDX,[p] // EDX: adresse der pixel
imul EBX,ECX,3 // EBX = 3 byte x anzahl pixel


@@again: // Hauptschleife. habe schon versucht, anstatt 3x BYTE-zugriffe
//1x DWORD zu nehmen, allerdings war die geschwindigkeit unverändert.
sub EBX,3

call RandX // eigene random-funtkion; ist schon 100% optimiert
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;
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.


Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic star
Beiträge: 122
Erhaltene Danke: 14



BeitragVerfasst: Mo 09.05.11 11:27 
user profile iconuser32 hat folgendes geschrieben Zum zitierten Posting springen:
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.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
bummi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 979
Erhaltene Danke: 124

XP - Server 2008R2
D2 - Delphi XE
BeitragVerfasst: 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
ausblenden Delphi-Quelltext markieren
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;
// Wahlfreier Zugriff über zuvor bestimmte letzte Scanline 47 ms
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
DRY DRY KISS
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic star
Administrator
Beiträge: 8370
Erhaltene Danke: 244

W2k, WXPpro
TP3 - D7pro
BeitragVerfasst: Mo 09.05.11 14:25 
Moin!

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
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. ;) :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15833
Erhaltene Danke: 737

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: Mo 09.05.11 15:18 
user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
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
Deine Version ist langsamer als seine. Denn er rechnet gar nichts, sondern geht immer nur ein Pixel weiter. ScanLine wird nur am Anfang einmal aufgerufen. Du jedoch rechnest jedesmal beim Pixelzugriff, das sollte deutlich langsamer sein. ;-)
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
user32 Threadstarter
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Mo 09.05.11 15:29 
Hi,
user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
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?

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
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:
ausblenden Delphi-Quelltext markieren
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 // (in FormCreate wird außerdem Randomize aufgerufen)
mov ECX,RandSeed // EAX und ECX = seed

imul EDX,ECX,87654321h // Seed * irgendwas -> EDX
mov RandSeed,EDX // neuer seed zurück
mul EDX // EAX*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.

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:

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. :crying:

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Moin!

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
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. ;) :idea:

cu
Narses

Ich werds mir gleich mal angucken!

Mfg

---Moderiert von user profile iconNarses: 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.
ausblenden volle Höhe Delphi-Quelltext markieren
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 // zum takte zählen
mov DWORD PTR[time1+$4],EDX // 64bit variable
mov DWORD PTR[time1+$0],EAX //


mov EAX,width
mov ECX,height
imul ECX,EAX // anzahl der pixel
mov EDX,[p] // EDX: adresse der pixel
imul EBX,ECX,3 // EBX = 3 byte x anzahl pixel

mov ESI,EDX // kein PUSH EDX mehr = schneller


@@again: // Hauptschleife.
sub EBX,3
(*random: keine ahnung, ob "LFSR" richtig ist, aber es sieht zufällig aus :) *)
mov EAX,RandSeed
mov EDI,$409
xor EAX,EDI
rol EAX,1
INC EAX
mov RandSeed,EAX
mul EDI
(*random end*)
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;
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
home home