AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Performance Inline Assembler vs. Objekt Pascal
Thema durchsuchen
Ansicht
Themen-Optionen

Performance Inline Assembler vs. Objekt Pascal

Ein Thema von OLLI_T · begonnen am 19. Nov 2003 · letzter Beitrag vom 20. Nov 2003
Antwort Antwort
OLLI_T

Registriert seit: 13. Okt 2003
Ort: Nähe Wetzlar / Hessen
143 Beiträge
 
Delphi 5 Enterprise
 
#1

Performance Inline Assembler vs. Objekt Pascal

  Alt 19. Nov 2003, 13:26
Hallo Leute!

Ich habe eben zur geistigen Erbauung und Unterstützung der Verdauung meine Routine, mit der ich
eine Speicherstelle in meiner Bitmap (24 Bit Farbtiefe) bei gegebener Füllfarbe FColor blende, in Assembler übersetzt. Dabei sind pCol der Zeiger auf RGB im Speicher und FAC der Blending-Faktor im Bereich 0..1. So ...:

Delphi-Quellcode:
// Blendet ein Pixel mit Füllfarbe FColor und Faktor FAC=X/255=0..1
Procedure TDIBSection24.BlendColFACPtrF(pCol:PByte; FAC:Double);
Var Temp:Integer;
  asm
    // Init
    push ebx
    push esi
    mov esi, eax
    mov ebx, edx

    xor eax, eax
    xor edx, edx

    mov al, Byte([esi].FColor)
    mov dl, [ebx]
    sub eax, edx
    mov Temp, eax
    fild Temp
    fmul FAC
    sub esp, $04
    fistp DWord Ptr[esp]
    fwait
    pop eax
    add [ebx], al

    inc ebx
    mov al, Byte([esi].FColor+1)
    mov dl, [ebx]
    sub eax, edx
    mov Temp, eax
    fild Temp
    fmul FAC
    sub esp, $04
    fistp DWord Ptr[esp]
    fwait
    pop eax
    add [ebx], al

    inc ebx
    mov al, Byte([esi].FColor+2)
    mov dl, [ebx]
    sub eax, edx
    mov Temp, eax
    fild Temp
    fmul FAC
    sub esp, $04
    fistp DWord Ptr[esp]
    fwait
    pop eax
    add [ebx], al

    pop esi
    pop ebx
End;

Begin
  pCol^:=pCol^+Round((TColorARGB(FColor).R-pCol^)*FAC); Inc(pCol);
  pCol^:=pCol^+Round((TColorARGB(FColor).G-pCol^)*FAC); Inc(pCol);
  pCol^:=pCol^+Round((TColorARGB(FColor).B-pCol^)*FAC);
End;
Ok, funktioniert beides sehr fix. Aber die Pascal Variante ist um mehr als 10% schneller als mein Assembler Code. Obwohl ich definitiv Befehle gespart habe. Der Call Round fehlt bei mir und die Register eax, edx werden nur anfangs genullt während der Compiler das für jeden Farbwert durchführt. Des weiteren wird von Round nen 64Bit Integer auf den Stack gepush während ich nur nen DWord nach eax übertrage.

Jetzt die Frage: WARUM ist mein Code langsamer in der Ausführung? Gibt es da intere Spielregeln, die ich nicht kenne? Ist nicht das erste Mal, dass mir eben diese aufgefallen ist.

Obwohl ich damit auch gleichzeitig ne Lanze für Delphi brechen kann. Da brummt schon ein guter Motor unter unserer Haube.

Viele Grüsse

OLLI
No Pain No Gain!
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Performance Inline Assembler vs. Objekt Pascal

  Alt 19. Nov 2003, 13:37
Ein Compiler kann in den meisten aller Fälle besser optimieren, als der Programmierer von Hand. Deswegen ist es nur in den seltentens Fällen sinnvoll eine Routine selbst in ASM zu schreiben.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
OLLI_T

Registriert seit: 13. Okt 2003
Ort: Nähe Wetzlar / Hessen
143 Beiträge
 
Delphi 5 Enterprise
 
#3

Re: Performance Inline Assembler vs. Objekt Pascal

  Alt 19. Nov 2003, 13:53
Hallo Luckie!

Bitte keine qualitativen Aussagen! Im November regnet es auch meistens und es ist dehalb nachts kälter als draussen.

Mein Code und der des Compilers sind nahezu identisch. Ich schau mir immer an was der Compiler gebaut hat, bevor ich in Assembler Code. Nur deshalb habe ich mich zu diesem Posting entschlossen. Hab schon des öfteren STRG-Y gedrückt und meinen Assembler Code gelöscht, weil der Compiler schnelleren und eleganteren Code gebaut hat.

Gruss

OLLI
No Pain No Gain!
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#4

Re: Performance Inline Assembler vs. Objekt Pascal

  Alt 19. Nov 2003, 14:19
Zitat:
Ein Compiler kann in den meisten aller Fälle besser optimieren, als der Programmierer von Hand
Was bedeuten würde das eine Maschine intelligenter als ein Mensch wäre. Dem stimme ich also nicht zu. Ein Mensch wird IMMER besseren Code erzeugen als ein Compiler, es ist nur eine Frage des Aufwand-Nutzen Verhältnisses dem der Mensch unterlegen ist. Das Problem ist das ein Mensch der einen Compiler übertrumpfen will exakt wissen muß wo der Compiler schlecht arbeitet. Nur wenn es dann offensichtlich ist das der Compiler ineffizienten Code erzeugen muß, zB. weil er neuere CPU Befehle nicht benutzt, lohnt sich der Einsatz von manuellem Assembler.

Was ich bisher sehen konnte ist in
Delphi-Quellcode:
    mov al, Byte([esi].FColor) 1.)
    mov dl, [ebx] 2.)
    sub eax, edx

    mov Temp, eax 3.)
    fild Temp
    fmul FAC
    sub esp, $04
    fistp DWord Ptr[esp]
    fwait 4.)
    pop eax 5.)
    add [ebx], al 6.)
1.) 8 Bit und 32 Bit Befehle und Speicherzugriffe werden vermischt. Dies ist auf 32 Bit CPU's tödlich.
2.) zwei Speicherzugriffe nacheinander und sofortiges benutzen dieser Register in SUB EAX,EDX ist tödliche für die CPU Pipelines, es wird Branches und Stalls geben da das SUB EAX,EDX auf die vorherigen langsammen Speicherzugriffsbefehle warten muß.
3.) ein Register wird wieder in den Stack gespeichert
4.) fwait kann raus, unter neueren CPU's > 286 eh sinnlos.
5.) Stack wird geupdated, POP bewirkt einen Speicherzugriff + ein Inkrement von ESP. besser also eine Stackvariable mit MOV EAX,[ESP] benutzen
6.) wieder ein 8 Bit Speicherzugriff

Gruß Hagen
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#5

Re: Performance Inline Assembler vs. Objekt Pascal

  Alt 19. Nov 2003, 14:34
Also fangen wir mal an zu optimieren. Das erste was wir dabei machen ist DENKEN

Dein Code multipliziert jeweils einen 8 Bit Farbwert mit einem Double Wert. Ist das überhaupt nötig ?? Würde ein Menschliches Auge einen Unterschied bemerken wenn die Funktion nicht ganz exakt rechnet ??

Also die Floatingpoint Operationen müssen raus und durch Integer Operationen ersetzt werden.

Kritisch auf heutigen CPU's ist die Feststellung das diese CPU so clever konstruiert wurden das sie zukünftige Speicherzugriffe schon vor der Ausführung von Code erkennen kann. Zwangsläufig ergibt sich daraus das man Speicherzugriffs-Operationen als Block von Befehlen staffeln sollte und reine Registerberechnungen ebenfalls staffeln sollte. D.h. erst werden die Daten per Speicherzugriff in soviele Register wie möglich geladen, und danach mit den Registern gerechnet, und abschließend die Daten wieder gespeichert. Es ist also sinnvoller lieber in Register geladene Daten umzuordnen, als sie Byte für Byte zu laden. Der 2. Block mit den Berechnungen kann dann so gecodet werden das man voneinander unabhänige Berechnungen durchführen kann. Dies nennt man Instrctionshuffling und bewirkt das die CPU sozusagen Befehle in paralell abarbeiten kann. Alle neueren CPU's haben mehrere Pipelines, in denen sozusagen mehrere Instruktionen paralell dekodiert und ausgeführt werden können. Der Unterschied zu Multi-CPU-Rechnern ist aber das diese Piplines nur abhänig von den verwendeten Instruktionen tatsächlich parallel arbeiten können. D.h. nicht jede Reiehenfolge von Instruktionen im Code kann parallel ausgeführt werden !

Nun, wenn wir davon ausgehen das in einer Scanline einer 24Bit Bitmap jeweils RGB Bytes stehen, und wissen das jede Scanline einer Windowsbitmap immer ein Vielfaches von 4 Bytes ist, egal wieviele Pixel in der Scanline stehen, dann wissen wir auch das ein 4 Bytes Speicherzugriff auf die RGB Farbwerte absolut legal ist. Man lädt also in EAX alle 4 RGB Werte in einem Rutsch und "entschlüsselt" sie dann von Register zu Register.

Oder besser noch: wenn wir wissen das das MMX Feature der CPU's exakt dafür konstruiert wurde die RGB Werte eines Speicherbereiches in parallel zu benutzen, dann ist es besser den Source gleich in MMX zu coden.

Suche mal nach den Graphics32 Komponenten im WEB, diese enthalten schon solchen Assembleroptimierten Code.

Gruß hagen
  Mit Zitat antworten Zitat
OLLI_T

Registriert seit: 13. Okt 2003
Ort: Nähe Wetzlar / Hessen
143 Beiträge
 
Delphi 5 Enterprise
 
#6

Re: Performance Inline Assembler vs. Objekt Pascal

  Alt 19. Nov 2003, 15:05
Hallo Hagen!

Vielen Dank für Deine konstruktiven Hinweise! Zu meiner Ehrenrettung muss ich jedoch sagen, dass ich so ab und an gedacht ... und vor allen Dingen die virtuelle Stoppuhr gedrückt habe.

Ich habe in meiner Klasse mehrere Funktionen zum Blenden. Welche ich nutze richtet sich danach in welchem Format der Blending-Faktor ursprünglich anfällt. Die Umrechnung in den Bereich 0..255 und dann mit Integerzahlen rechnen dauert effektiv länger! Das hab ich getestet.

Für die Default Füllfarbe (mit Alphakanal) führe ich Vorberechnungen durch und ersetze dann auch die Division mit 255 durch SHR 8. Das bringt Schwung in den Laden!

Ist mir schon klar, dass 24Bit irgendwo zwischen den Stühlen stehen. Ich habe lange mit der G32 Bibliothek von Alex Desimov gearbeitet. Aber für meine Zwecke sind das meistens 25% verschenkter Speicher. Stattdessen hab ich den Alpha-Kanal ausgelagert und eine Masken-Klassen daraus gebastelt, die ich bei Bedarf über die Bitmap legen kann.

Jetzt hab ich aber noch ein paar Fragen und Anmerkung zu Deiner Liste 1 - 6:

Zitat:
3.) ein Register wird wieder in den Stack gespeichert
Gibt es eine Möglichkeit, den Inhalt eines Registers (eax) direkt in die FPU und umgekehrt zu transferieren?

Zitat:
4.) fwait kann raus, unter neueren CPU's > 286 eh sinnlos.
5.) Stack wird geupdated, POP bewirkt einen Speicherzugriff + ein Inkrement von ESP. besser also eine Stackvariable mit MOV EAX,[ESP] benutzen
Den Code hab ich aus der Fkt Round geklaut. Vorher hab ich über meine lokale Variable Temp gearbeitet. Das war noch langsamer.

Viele Grüsse + nochmals danke

OLLI
No Pain No Gain!
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#7

Re: Performance Inline Assembler vs. Objekt Pascal

  Alt 20. Nov 2003, 14:15
Zitat:
Gibt es eine Möglichkeit, den Inhalt eines Registers (eax) direkt in die FPU und umgekehrt zu transferieren?
Nicht das ich wüsste. Deshalb sollte man Floatingpoints vermeiden, und in deinem Falle ist die aus möglich.

Gruß Hagen
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:08 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz