Autor |
Beitrag |
Boldar
      
Beiträge: 1555
Erhaltene Danke: 70
Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
|
Verfasst: Fr 18.09.09 14:21
Hallo,
Wie wird TColor intern gespeichert?
Ich möchte über assembler an die Werte für rgb rankommen, aber am besten ohne die funktion rgb.
Ich hab mal gehört, das sei etwa so:
Delphi-Quelltext 1: 2: 3:
| TColor = packed record r, g, b: byte; end; |
Stimmt das?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Fr 18.09.09 14:25
Das Doppelte type ist hier vollkommen korrekt
Delphi-Quelltext 1:
| type TColor = type LongInt; |
Die Darstellung isr $AABBGGRR. Wie du das nun mit ASM machst, sollte sich daraus hoffentlich ergeben. Ansonsten: Wozu brauchst Du das genau? Weil ggf. gibt's da einfachere Möglichkeiten...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Boldar 
      
Beiträge: 1555
Erhaltene Danke: 70
Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
|
Verfasst: Fr 18.09.09 14:32
ich will ne bildanalyse machen.
konkret will ich folgende Operationen mit den Farben durchführen:
Quelltext 1: 2:
| r:=r-((g div 2)+(b div 2)) c:=r>eineconstante |
Allerdings liegen die Farben dabei in einem array von Pixeln (wie in TBitmap.canvas.pixels) und c liegt dann in einem
Delphi-Quelltext 1:
| array [0..maxx, 0..maxy] of boolean |
Das soll dann halt für jeden Wert durchgeführt werden.
Ich hatte mir nun mal vorgenommen, das mittels assembler zu machen(weniger wegen der Geschwindigkeit als vielmehr zum üben).
Edit: Überlegung: Danach berechne ich den Mittelwert der x- und ywerte, wo der eintrag [x, y] im zweitem array true ist. Eigentlich bräuchte ich die Werte doch nicht erst speichern, oder?
Dann bräuchte ich auch nur eine Schleife.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Fr 18.09.09 20:28
Boldar hat folgendes geschrieben : | ich will ne bildanalyse machen.
konkret will ich folgende Operationen mit den Farben durchführen:
Quelltext 1: 2:
| r:=r-((g div 2)+(b div 2)) c:=r>eineconstante |
Allerdings liegen die Farben dabei in einem array von Pixeln (wie in TBitmap.canvas.pixels) und c liegt dann in einem
Delphi-Quelltext 1:
| array [0..maxx, 0..maxy] of boolean | |
Bzgl. Deiner Analyse:
Könnte man (unter Nutzung nur eines Registers so machen:
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:
| asm AND EAX, $00FEFEFF SHL EAX, 7 BSWAP EAX ADD AL, AH BSWAP EAX ADC AL, AL ADD EAX, EAX AND EAX, $FF00FFFF ROL EAX, 8 JMP @@l2 @@l1: SUB EAX, $00010001 @@l2: TEST EAX, $0000FFFF JZ @@l3 TEST EAX, $FFFF0000 JNZ @@l1 NEG EAX JMP @@l4 @@l3: SHR EAX, 16 @@l4: SUB EAX, DeineKonstante SETC AL end; |
Der ist zwar ABSOLUT NICHT OPTIMAL, aber arbeitet in genau einem CPU-Register (wollte das nur mal ausprobieren, ob das geht ^^)
Zur Erklärung: Ich schieb mir in EAX die Bits ein wenig so umher, dass ich allein mit den Unterregistern AL und AH die Addition ausführen kann. Das durch 2 Teilen mach ich mit dem SHL 7-Befehl (und dem AND-Befehl vorher zur Maskierung). Source ist ungetestet, dürfte aber funktionieren ... Hab's nicht getestet
Boldar hat folgendes geschrieben : | Das soll dann halt für jeden Wert durchgeführt werden. |
Dafür hast Du bei mir ja noch EDX und ECX zur Verfügung
Boldar hat folgendes geschrieben : | Ich hatte mir nun mal vorgenommen, das mittels assembler zu machen(weniger wegen der Geschwindigkeit als vielmehr zum üben). |
Übung 1: Meinen Source nachvollziehen
Übung 2: Den ohne zusätzliche Register oder Speicher zu nutzen optimieren
Scherz bei Seite ^^
Boldar hat folgendes geschrieben : | Edit: Überlegung: Danach berechne ich den Mittelwert der x- und ywerte, wo der eintrag [x, y] im zweitem array true ist. Eigentlich bräuchte ich die Werte doch nicht erst speichern, oder?
Dann bräuchte ich auch nur eine Schleife. |
Jain ... Man kann den Mittelwert durchaus ohne die Einzelelemente zu speichern ermitteln. Dann musst du aber zumindest die Anzahl der 1en und die Summe der Positionen, an denen diese standen dir merken. Also 2 Variablen, statt einer gesamten Zeile oder dem Gesamtbild.
//Edit: Paar kleine Ergänzungen ...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 21.09.09 10:56
Also zum Lernen von Assembler kann man sich auch mal anschauen was der Kompiler da drauß macht. Also einen Breakpoint setzen und in der CPU Ansicht schauen was gemacht wird. Denn ich muss ehrlich gestehen. Der Assembler Code den du (benbe) da gemacht hast erscheint mir doch etwas komplexer als er sein müsste. Und da kann ich nur mal wieder sagen, dass Assembler keine ultimatives Heilmittel für alles mögliche ist. Was der Delphikompiler aus dem Code produziert ist vielleicht nicht immer optimal aber sehr häufig doch schon recht anständig.
In den meisten Fällen genügt normaler Hochsprachen Code auch schon vollkommen aus um performante Anwendungen zu erzeugen. Wirklich Sinnvoll ist Assembler meiner Meinung nur noch da wo man auf spezielle Maschinen Befehle zugreifen muss oder wo Geschwindigkeit wirklich fundamental wichtig ist. Bitte nicht falsch verstehen. Nichts desto trotz finde ich es wichtig zu wissen was auf der CPU passiert. Aber meistens genügt eine simple Umstellung. Und zwar ohne, dass man alles auf Assembler neu schreiben muss. Zu mal durch so etwas und mangelndes Wissen auch schnell eine ziemlich spannende Situation für den Endanwender entstehen kann!
Aus folgendem Code
Delphi-Quelltext 1: 2: 3: 4: 5:
| var RGB: TRGBQuad; C: Boolean; begin C := RGB.rgbRed - (RGB.rgbGreen shr 1 + RGB.rgbBlue shr 1) > 0; |
Wird solch ein Assembler Code erstellt.
Quelltext 1: 2: 3: 4: 5: 6: 7:
| 004552B1 0FB6442401 movzx eax,[esp+$01] 004552B6 D1E8 shr eax,1 004552B8 0FB61424 movzx edx,[esp] 004552BC D1EA shr edx,1 004552BE 03C2 add eax,edx 004552C0 0FB6542402 movzx edx,[esp+$02] 004552C5 2BD0 sub edx,eax |
In EDX befindet sich anschließend das Ergebniss. In diesem Fall ist das was Delphi erstellt hat nicht ganz optimal, da jeweils 1 Byte aus dem Speicher gelesen wird. Besser wäre es, wenn der gesammte Inhalt auf 1 Mal gelesen werden würde. Aber falls man das per Hand machen wollte würde es auch genügen, wenn man den Farbwert ein Mal komplett in ein Register packt und dann via L, H etc darauf zugreift. Dadurch würde man sich aber nur 2 Speicherzugriffe ersparen. Und zwar Zugriffe auf Speicher der sich im Prozessorcache befindet. Aber bevor man an einem Punkt angelangt ist an dem man darauf rumreiten kann muss man sich auch erst mal den Rest seines Codes zu Gemüte führen.
PS: Und ja ich weiß, dass das Thema ein paar Tage alt ist. Aber ich denke meine oberen Hinweise sind wichtig genug um erwähnt zu werden.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 21.09.09 11:05
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 21.09.09 12:13
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
Boldar 
      
Beiträge: 1555
Erhaltene Danke: 70
Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
|
Verfasst: Mo 21.09.09 12:23
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 21.09.09 12:56
Lossy eX hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | Ist auch (ich hoffe, dieser Teil ist dir in meinem Post aufgefallen) nicht als Optimum gedacht. Vielmehr ging's mir darum, das in möglichst wenigen Registern abzuhandeln. |
Hab ich gesehen. Aber wozu? Um Speicher durch ignorieren einzelner Register zu sparen? Ja klar. Die Register sind fast immer zu wenig und man sollte immer Sparsam damit umgehen. Aber wenn sie frei sind, dann kann man sie doch auch nutzen. Zu mal so etwas den Lerneffekt nicht erhöht sondern eher was anderes zeigt.... |
Soetwas kann insbesondere dann nötig sein, wenn die Verwendung von Speicherzugriffen zu Cache Misses führt, oder man die anderen Register nicht zur Verfügung hat (daher mein Hinweis, dass man in EDX und ECX die Zählvariablen unterbringen könnte).
Lossy eX hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | Lossy eX hat folgendes geschrieben : | Was der Delphikompiler aus dem Code produziert ist vielleicht nicht immer optimal aber sehr häufig doch schon recht anständig. |
Wenn man NOP-Äquivalente als Code bezeichnet durchaus Geb Dir da aber Recht ... |
Ich habe nop überwiegend nur zwischen Methoden gesehen. Und da stören die nur selten. Ich habe zwar auch schon ziemlich komisches Zeugs vom Kompiler gesehen allerdings wenn das nur 1-2 aufgerufen wird dann ist mir das ziemlich egal. |
Ich habe NOP-Äquivalente geschrieben ... Und der Delphi-Compiler (besonders ältere Versionen, bei neueren weiß ich's ATM nicht) haben gern mal Anweisungen wie MOV EAX, EAX und dergleichen im Code stehen gelassen.
Boldar hat folgendes geschrieben : | Lossy eX hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | Ist auch (ich hoffe, dieser Teil ist dir in meinem Post aufgefallen) nicht als Optimum gedacht. Vielmehr ging's mir darum, das in möglichst wenigen Registern abzuhandeln. |
Hab ich gesehen. Aber wozu? Um Speicher durch ignorieren einzelner Register zu sparen? Ja klar. Die Register sind fast immer zu wenig und man sollte immer Sparsam damit umgehen. Aber wenn sie frei sind, dann kann man sie doch auch nutzen. Zu mal so etwas den Lerneffekt nicht erhöht sondern eher was anderes zeigt....
|
Aber man muss die laufvariablen der 2d-Schleife, die summen aus x und y sowie den counter auch noch speichern.
Und wenn man zum analysieren nur 1 register braucht, passt es genau. |
Jap. Wobei bei 32-Bit-Alignment der Daten keine Lücken im Speicher entstehen und man daher mit einer einzigen, rückwärtslaufenden Schleife auskommen kann. Aber sonst ist das bereits richtig erkannt 
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 21.09.09 12:57
Boldar hat folgendes geschrieben : | Aber man muss die laufvariablen der 2d-Schleife, die summen aus x und y sowie den counter auch noch speichern.
Und wenn man zum analysieren nur 1 register braucht, passt es genau. |
Ähm. Nicht ganz. Du willst doch nicht die komplette Berechnung und Analyse in einer Methode und den paar Registern durchführen. Du brauchst nebenbei noch zwei Pointer. Einen Pointer auf die Bilddaten und einen auf dein Array in dem die Ergebnisse abgelegt werden. Empfehlenswert wäre es da die Methoden aufzuteilen. Eine Methode zum Berechnen des Arrays. Und eben eine Methode zum Auswerten deines 2D Arrays. Außerdem kann man die anderen Register (EDI, ESI, EBX) auch kurzfristig auf den Stack packen. EAX, ECX und EDX dürfen in Assemblermethoden verändert werden. Die anderen nicht. Also ein mal am Anfang der Methode sichern und am Ende wiederherstellen. Ich muss aber gestehen, dass ich dir da nicht 100%tige Angaben machen kann, da ich nicht genau weiß was du vor hast. Und besonders in so eine Fall ist es sehr wichtig wirklich detailierte Informationen zu besitzen.
Nichts desto trotz. Es bringt doch nicht, wenn man seinen Code zu sehr verbiegt und dadurch ein Problem gegen ein Anderes tauscht. Dadurch wird es dir deutlich schwerer Fallen zu verstehen was da passiert und wenn doch, dann hast du unnötig komplexe Konstrukte gelernt. Ob das so sinnvoll ist lasse ich mal im Raum stehen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
Boldar 
      
Beiträge: 1555
Erhaltene Danke: 70
Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
|
Verfasst: Mo 21.09.09 13:15
Ich will ja am schluss nur noch den mittelwert aller gültigen x und den aller gultigen y.
Da brauche ich garkein zwischenarray und auch keine 2te schleife, das spart enorm viel.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 21.09.09 13:20
BenBE hat folgendes geschrieben : | Soetwas kann insbesondere dann nötig sein, wenn die Verwendung von Speicherzugriffen zu Cache Misses führt, oder man die anderen Register nicht zur Verfügung hat (daher mein Hinweis, dass man in EDX und ECX die Zählvariablen unterbringen könnte). |
Ja. Das sind durchaus berechtigte Hinweise. Allerdings sehe ich nicht, dass die in diesem Fall auch nur ansatzweise greifen würden. Ein Bild liegt typischerweise an einem Stück im Speicher. Der CacheManager muss das also sowieso ein mal komplett in den Cache laden. Da kommt man so oder so nicht umher. Und ein Bild ist da um ein vielfaches besser als zufällige Speicherzugriffe. Alles anderen Register kann man am Anfang und am Ende ein mal Sichern bzw. Wiederherstellen. Da sehe ich noch lange keinen Grund darin den Code so stark zu verfremden. Zu mal Boldar daraus etwas lernen möchte.
BenBE hat folgendes geschrieben : | Ich habe NOP-Äquivalente geschrieben ... Und der Delphi-Compiler (besonders ältere Versionen, bei neueren weiß ich's ATM nicht) haben gern mal Anweisungen wie MOV EAX, EAX und dergleichen im Code stehen gelassen. |
Das ist richtig. So etwas habe ich auch schon gesehen. Allerdings überwiegend immer an Stellen an denen es für normalsterbliche keinen Unterschied macht. Also nie an wirklich kritischen Stellen. Und da dann mit Assembler zu kommen wäre doch etwas übertrieben.
Aber nichts destro trotz haben meine Hinweise hier etwas angefacht was weit von der eigentlichen Frage entfernt ist.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 21.09.09 17:15
Boldar hat folgendes geschrieben : | Ich will ja am schluss nur noch den mittelwert aller gültigen x und den aller gultigen y.
Da brauche ich garkein zwischenarray und auch keine 2te schleife, das spart enorm viel. |
Also ich würde generell vorschlagen, dass du als Erstes deinen Code in Pascal programmieren solltest. Und zwar so wie du ihn letzten Endes auch haben wolltest. Natürlich schon mit Optimierungen auf Maschinensicht. Also nur nötige Variablen, Schleifen etc. Anpassungen am Algorithmus sind so auch viel leichter durchzuführen und du siehst ob der Code auch dem entspricht wie das was du haben willst. Du sieht vor allem auch welche Variablen etc du brauchst. Dann hättest du einen konkreten Code den du als Basis für eine Umsetzung als Vorlage benutzen kannst.
Wenn du diesen Code hast und dann immer noch auf Assembler trimmen möchtest, und sei es nur zum Lernen, dann solltest du dir anschauen was Delphi da für einen Maschinencode hergestellt hat. Theoretisch könntest du das aus der CPU Ansicht auch direkt als Code benutzen. Natürlich nur die Assemblerbefehle selbst. Die Zeilen kann man dort markieren und kopieren und als Code einfügen. Adressen und Bytecode dann löschen. Damit hättest du schon eine Assemblervariante deines Codes. Den könnte man Optimieren bzw. nach eigenen Wünschen umstellen. Um eben Engpässe zu umgehen oder ungünstige Konstrukte zu vermeiden.
Für die Assemblermethode bzw größere Umstellungen an der Methode kannst du auch jeweils die Methode kopieren und unter neuem Namen einfügen. Dann hast du die anderen Versionen noch und kannst ggf. hin und her springen (zum Testen). Ich denke das könnte die sinnvollste Vorgehensweise sein. Auf diese Weise hättest du schon eine funktionierende Variante und müsstest nicht komplett bei null anfangen. Besonders, wenn man eher noch nichts groß mit Assembler gemacht hat, dann ist der Anfang doch sehr mühsam und verwirrend. Als ich erste Sachen in Assembler gemacht habe hatte ich die CPU Ansicht zu schätzen gelernt.
[edit] Achja. Dein Bild sollte idealerweise in 32 Bit pro Pixel vorliegen. Auf Assemblerebene dürfte das das vereinfachen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
|