Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Sa Jul 12, 2025 22:57

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 11 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: ASM in Delphi
BeitragVerfasst: Mi Mai 06, 2009 10:41 
Offline
DGL Member
Benutzeravatar

Registriert: So Jan 07, 2007 21:26
Beiträge: 130
Wohnort: mal hier mal da mal irgendwo
Hi liebe Gemeinde!

Zur Optimierung seiner Routinen ist man ab und an mit Assembler am besten beholfen...

Ich setz mich allerdings erst seit Anfang der Woche mit Inline - Assembler in Delphi auseinander...

Bevor ich auf mein Problem eingehe ein abgespecktes Beispiel:

Code:
  1.  
  2. type
  3.   TVector3 = packed object
  4.     private
  5.       fcomponents:array[0..2] of extended;
  6.     public
  7.       procedure Reset(px, py, pz:extended);
  8.   end;
  9.  
  10. implementation
  11.  
  12. procedure TVector3.Reset(px, py, pz:extended);
  13. asm
  14.   MOV fcomponents[0], px
  15.   MOV fcomponents[1], py
  16.   MOV fcomponents[2], pz
  17. end;
  18.  


So sieht das ganze (stark vereinfacht) aus ... Mein Problem ist nun dass folgene Fehlermeldung kommt:
Zitat:
Undeclared identifier: 'fcomponents'


So ... nach ewigem googlen hatte ich die Hoffnung aufgegeben und statt dem fcomponents-Array einfach 3 extended-Variablen genommen (fx, fy, fz: extended;)
Nach einem Thread auf Delphi-Praxis wo Inline-Assembler kurz angeschnitten wird, wusste ich, dass die 3 übergebenen Parameter automatisch in den
EAX, EDX und ECX gespeichert werden. Quasi müsste ja dann px in EAX, py in EDX und pz in ECX gelandet sein und folglich muss jedes der Register so groß sein dass eine Variable vom Datentyp extended hineinpassen würde.
Nun hatte ich den Quelltext wie folgt geändert:

Code:
  1.  
  2. type
  3.   TVector3 = packed object
  4.     private
  5.       fx,
  6.       fy,
  7.       fz: extended;
  8.     public
  9.       procedure Reset(px, py, pz:extended);
  10.   end;
  11.  
  12. implementation
  13.  
  14. procedure TVector3.Reset(px, py, pz:extended);
  15. asm
  16.   MOV fx, px
  17.   MOV fx, py
  18.   MOV fx, pz
  19. end;
  20.  


In den Variablen steht ja immernoch die übergebene Gleitkommazahl drinne und jetzt müsste es eigentlich fehlerfrei funktionieren ... Kompilieren und *Puff* Fehler:
Zitat:
Operand size mismatch


Wie kann es passieren dass der Speicher einer Extended-Variable eine andere Größe hat einer anderen Extended-Variable ôÔ

Ich hoffe ihr könnt mir bei meinem Problem weiter helfen...
Am liebsten wäre es mir wenn ich bei der ersteren Art und Weise mit dem fcomponents-Array bleiben könnte, muss aber nicht unbedingt sein.

Danke schonmal im Vorraus an die fleißigen Helfer ;)
cuz thebubble

_________________
Wenn Worte nichts als Worte sind,
dann müssen's Steine sein!
Solange - bis sie pleite sind - schmeißt Fensterscheiben ein!

- Fidl Kunterbunt -


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 10:55 
Offline
DGL Member
Benutzeravatar

Registriert: Di Sep 20, 2005 13:18
Beiträge: 1054
Wohnort: Dresden
Programmiersprache: C, C++, Pascal, OPL
Sicher, dass man in Assembler Gleitkommavariablen "einfach so" hin und herschubsen kann?
Davon abgesehen, habe ich mal gehört, dass Assembler heute kaum noch was bringt, weil der Compiler genug optimiert. Wobei Delphi ja ziemlich schlecht optimieren soll (Gab mal nen Vergleich Free pascal vs. Delphi bei identischem Code => FP 2x schneller)

Oder probier doch mal float oder so, vielleicht mag er ja auch nur keine großen Gleitkommazahlen.

Und zu dem ersten Problem: Ich glaube, auch in Delphi sind Arrays nichts anderes als Pointer auf das erste Element und der Rest wird mit Pointerrechnung gemacht.
D.h: bla[5] ist das gleiche wie Adresse von bla[0]*5*sizeof(bla[0]) (Also die größe EINES Elements).

Zumindest war es damals unter DOS so... Vielleicht sind meien Assemblererfahrungen da aber zu alt. :roll:

LG Ziz

_________________
Denn wer nur schweigt, weil er Konflikte scheut, der macht Sachen, die er hinterher bereut.
Und das ist verkehrt, denn es ist nicht so schwer, jeden Tag zu tun als ob's der letzte wär’.
Und du schaust mich an und fragst ob ich das kann.
Und ich denk, ich werd' mich ändern irgendwann.

_________________Farin Urlaub - Bewegungslos


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 11:37 
Offline
DGL Member
Benutzeravatar

Registriert: So Jan 07, 2007 21:26
Beiträge: 130
Wohnort: mal hier mal da mal irgendwo
Also ... das mit dem Array hab ich jetzt seingelassen ...

Nun aber noch folgendes...

Mit single ging mein inline asm ... dann hab ich mal geguckt, wie groß single is ... 4 byte, dann hab ich mich daran erinnert das in dem einem tut der befehl CDQ war, der die register auf 64 bit erweitert, 64/8 = 8 wenn ich nich ganz doof bin ... quasi 8 byte. dann geguckt wie groß double ist, 8 byte... ob CDQ oder nich double passt nich rein... Aber warum?

cuz thebubble

_________________
Wenn Worte nichts als Worte sind,
dann müssen's Steine sein!
Solange - bis sie pleite sind - schmeißt Fensterscheiben ein!

- Fidl Kunterbunt -


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 12:29 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Heyo bubble

Also, zuerst etwas zu der Aufrufkonvention "register", die bei Pascal üblich ist.
Das mit den ersten drei Parametern in eax, ecx und edx ist eine Halbwahrheit. Ich habe mich damit vor kurzem genauer auseinandergesetzt und werde noch viel damit zu tun haben in nächster Zeit.
Welche Parameter in die drei Register kommen, hängt von der Größe der einzelnen Paramter ab. Es kommen die ersten drei Parameter, die kleiner oder gleich der Registergröße sind. Unter Delphi daher alles, was kleiner oder gleich Pointer/LongInt/Single/... (wobei Single als Floating Point wert schon wieder ein Sonderfall ist) ist.
Du benutzt Extended, welches meines wissens nach 10 Bytes groß ist, auf jeden fall aber mehr als 8 Bytes (Double). Das passt auf keinen Fall in ein Register. In solchen Fällen, wo die Parameter direkt übergeben werden sollen und größer als 4 byte sind, werden die meistens aufgeteilt und nacheinander auf den Stack gepackt. Ein Int64 (8Byte) zum Beispiel landet als zwei getrennte LongInts (4Byte) aufm Stack (auch die Stackslots werden als 32-bit/4byte behandelt).
Noch ein Beispiel zum Abschluss:
Code:
  1. procedure (A: LongInt; B: Int64; C: Word; D: QWord; E: String);

Die Parameter würden so abgelegt:
A => eax
B => (zwei Stackslots)
C => ecx
D => (zwei Stackslots)
E => pointer auf E in edx
(bei E bin ich mir nicht 100%ig sicher, mit Strings und register hab ich mich noch nicht beschäftigt, aber da Strings auch nur Pointer sind, müssten die eigentlich in nem Register landen)

So. Floating Points. Für die Berechnung von Floating Point werden (Single, Double, Extended) gibt es separate Register. Allerdings müssten die Daten dieser Floating Points meines wissens nach ohne weiteres in normalen Registern abspeicherbar sein, sofern diese groß genug dafür sind (d.h. nur ein Single passt in ein Register). Man kann natürlich mehrere Register miteinander verbinden und gemeinsam nutzen. Aber das ist nur bedingt sinnvoll. Bei Floats wegen der separaten Register für diese sicherlich nicht.

Dann zu dem, was du ganz am anfang gesagt hast:
thebubble hat geschrieben:
Zur Optimierung seiner Routinen ist man ab und an mit Assembler am besten beholfen...

Das stimmt auch nur unter sehr speziellen Bedingungen, die bei dir nicht gegeben sind. Mir ist bisher nur ein Fall klar geworden, wo man *eventuell* besser sein kann als der Compiler (der wirklich sehr sehr viel Optimiert. Du musst dir allein mal den ASM-Output anschauen (beim Debuggen im CPU fenster)) und dieser Fall ist dann, wenn du bspw. die Kontrolle über den Stack brauchst. Das kann passieren, wenn du einen "manuellen" Methodenaufruf machen willst oder sowas. Aber wie gesagt, meistens ist der Compiler damit viel besser als ein durchschnittlicher ASM-Programmierer. Ein anderer Fall ist, wenn du eine Rechenoperation auf mehrere Werte gleichzeitig anwenden musst. Da sind dann die MMX-Operationen gold wert. Vorallem birgt x86 Assembler eine menge Tüken, beispielsweise funktioniert folgendes nicht:
Code:
  1.  
  2. mov EAX, ECX
  3. mul 10
  4.  

Aber folgendes wiederum geht:
Code:
  1.  
  2. mov EAX, 10
  3. mul ECX
  4.  

(ich hoffe, ich verdreh hier gerade die operanden nicht, ich bin AT&T gewöhnt)
erstes geht nicht, einfach weil die Kombination nicht vorgesehen ist. Frag mich nicht warum.

Ich hoffe, dir geholfen zu haben.

Gruß Lord Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 13:44 
Offline
DGL Member
Benutzeravatar

Registriert: So Jan 07, 2007 21:26
Beiträge: 130
Wohnort: mal hier mal da mal irgendwo
Okay dass der Compiler da besser optimiert hätte ich nicht gedacht >.<
Also kann ich das ganze schonmal wieder verwerfen ^^'

Allerdings nur um noch beim Thema zu bleiben ... Auf die FPU bin ich auch noch gestoßen ...
Und auch eine recht gute Erklärung dazu ... Aber irgendwie kommt bei mir nen ganz komisches Ergebnis >.<

Also es geht um ne ganz normale Addition:

Code:
  1.  
  2.   FILD &a
  3.   FILD &b
  4.   FADD ST, ST(1)
  5.   FSTP &Result
  6.  


(a und b sind vom Typ single, also 4 Byte)

So wenn ich jetzt mit Breakpoints arbeite dann ist a=1,5 und b auch ... 1,5+1,5 is ja bekannterweise 3
Wenn ich dann aber das Ergebnis ausgebe kommt 2139095040 raus :?
Versteht das jmd?

cuz bubble

_________________
Wenn Worte nichts als Worte sind,
dann müssen's Steine sein!
Solange - bis sie pleite sind - schmeißt Fensterscheiben ein!

- Fidl Kunterbunt -


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 13:49 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
FILD ist für Integer, was du suchst müsste FLD sein.

Gruß Lord Horazont

P.S.: Ein paar deiner Terragen (ist doch Terragen, oder? Ansonsten: wie hast du das bitte gemacht? :D)-Bilder sind ja echt genial.

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 14:14 
Offline
DGL Member
Benutzeravatar

Registriert: So Jan 07, 2007 21:26
Beiträge: 130
Wohnort: mal hier mal da mal irgendwo
Boah wo doof kann man denn sein >.< nen I zu übersehen ... knapp 2 stunden lang >.<

Gut mit FLD klappts einwandfrei ^^

*Schreck* das alte ding namens Website is ja noch online ^^' ganz vergessen ^^
Ja die unter "my renders" sind alle mit TG, bis auf 5 Bilder alles mit TG 0.9 Free 3 mit der TG 2 TP ... Die 5 werden aber alle noch mal neu gerendert, da man jetzt bessere qualitative Renderings mit TG 2 machen kann (also mit der TG 2 Free (beta))

cuz bubble

_________________
Wenn Worte nichts als Worte sind,
dann müssen's Steine sein!
Solange - bis sie pleite sind - schmeißt Fensterscheiben ein!

- Fidl Kunterbunt -


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 14:16 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Die Kompiler leisten derzeit schon wirklich sehr sehr gute Arbeit. Wenn man nicht gegen den Kompiler arbeitet sondern mit Ihnen, dann steckt wirklich sehr viel Potential im normalen Quellcode. Ich für meinen Teil schaue mir von verschiedenen wichtigen Codes auch gerne mal die CPU Ansicht an um zu verstehen was dort eigentlich genau passiert. Und mit ein bisschen Übung sieht man oftmals, dass man durch Kleinigkeiten dem Kompiler helfen kann. Und dann ist meistens gar kein Assembler mehr nötig. Es sei denn man möchte so etwas wie MMX oder SSE benutzen. Dann kommt man nicht drumherum.

Also Beispiel wäre ein Record mit 3 Extended oder Double Werten. Wenn man dieses an eine Methode übergibt sollte man grundsätzlich mit var oder const arbeiten. Denn sonst muss die Methode immer erst einmal den Speicherbereich kopieren. Mit const wird nur der Pointer übergeben und dieser wird dann auch für lesende Zugriffe verwendet. Die einzelne Addition wird vom Kompiler meistens eh schon immer so simpel gehalten wie möglich. Das macht mitunter schon sehr viel aus. Das hatte ich auch in einer Vektorunit schon gesehen. Assembler wurde wie wild eingesetzt aber an simpelsten Stellen wurde es verpennt var oder const zu benutzt. Was mal eben 20-30% an Leistung gebracht hätte. Nur weil der Kompiler dann besser über den Code bescheid wusste.

Wenn man zum Beispiel einem Record ein anderes, gleichen Types, zuweisen will, dann optimiert der Kompiler solche Zuweisungen auch sehr gut. In Anhängigkeit der Recordgröße kommen dann auch die FPU Register als transferregister zum Einsatz. Besser kann mans kaum machen. Dabei muss man aber "Record := Record" machen. Wenn man jedes Feld einzeln zuweist, dann nimmt man dem Kompiler das Potential am Code etwas zu optimieren.

Wichtig ist vor allem auch rauszufinden wo das Potential zum Optimieren steckt. Es nützt nichts etwas optimieren zu wollen was sowieso schon kein Problem darstellt. Bzw wenn andere Stellen daneben ein viel größeren Problem darstellen.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 14:46 
Offline
DGL Member
Benutzeravatar

Registriert: Di Sep 20, 2005 13:18
Beiträge: 1054
Wohnort: Dresden
Programmiersprache: C, C++, Pascal, OPL
Ich hatte mal einen Fall, wo ich einfach nur eine wagerechte Linie malen wollte.
Die Idee ist natürlich simpel:
Code:
  1. for a:=0 to 319 do
  2. pixel[a]=farbe;

In Assemlber (ARM) war das ganze dann auch nicht schneller - wie zu erwarten.
ABER!
Wenn man statt einer Schleife wirklich 320 mal (halt in Assembler) die Farbe eines Pixels setzt, ist es schneller - dreimal so schnell. Grund?
Bei der normalen Variante läuft es so ab:
Code:
  1.  
  2. Farbe setzten
  3. Zahl erhöhen
  4. Vergleichen, ob 320 erreicht ist, sonst zurückspringen
  5.  

D.h. 320 mal wird die Zahl erhöht und ein Vergleich mit 320 durchgeführt.

Natürlich mir klar, dass man die Schleifenidee mit ARM/X86-spezifischen Eigenarten noch verbessern kann und nicht wirklich alle 320 Sachen "von Hand" reinschreiben muss unbedingt. Aber die Grundidee ist klar, denke ich.

Btw. noch eine Frage, die zu dem Thema passt, imho: Müsste das rechnen mit Floats dann nicht schneller sein als mit double geschweige denn extended?

LG Ziz

_________________
Denn wer nur schweigt, weil er Konflikte scheut, der macht Sachen, die er hinterher bereut.
Und das ist verkehrt, denn es ist nicht so schwer, jeden Tag zu tun als ob's der letzte wär’.
Und du schaust mich an und fragst ob ich das kann.
Und ich denk, ich werd' mich ändern irgendwann.

_________________Farin Urlaub - Bewegungslos


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mi Mai 06, 2009 15:14 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Nur kurz ein kleiner Hinweis zu den Aussagen von mir oben: Das bezieht sich natürlich alles nur auf 32-Bit-Code. Auf einer 64er Plattform dürfte das ganze schon allein wegen der größeren Anzahl der Register und dem anderen Befehlssatz anders aussehen.

Ich weiss nichts über die Rechenperformance von Single, Double und Extended, aber ich weiss, dass Extended auf einigen Plattformen (meine, dass mal in irgendeinem Teil der FPC-Doku gelesen zu haben) problematisch sein kann (wegen seiner krummen Größe von 10 Byte).

Gruß Lord Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Do Mai 07, 2009 08:36 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Ziz: Es ist natürlich klar, dass es schneller ist, wenn du anstelle einer Schleife einen festen Code hast der 320 Pixel zeichnet. Eben weil du keine Schleife mehr hast. Aber so etwas kann man nur 1-2 Fällen wirklich einsetzen. Bei den anderen Fällen ist es eher nicht praktikabel. Es wäre aber eventuell auch möglich solche Dinge zu verbinden. Also flexibel und reduzierte Schleifendurchläufe. Und zwar in dem man 2 Schleifen macht. Die erste Schleife beinhaltet 8 Schleifenkörper auf einmal. Würde also 8 Pixel am Stück malen. Und die zweite Schleife (die danach ausgeführt wird) zeichnet einzelne Pixel. Also alle Pixel, die noch fehlen um den Count voll zu bekommen. Was dann maximal 7 Stück sind. Problem dabei ist nur. Man muss den Code kopieren was ihn wieder anfälliger macht. Anstelle von 8 kann man auch andere Werte nehmen. Eine Potenz von 2 wäre aber besser für die CPU. So etwas in der Art wird auch beim Kopieren von Speicher gemacht.

Single, Double oder Extended. Jain. Singles nehmen zwar weniger Platz weg. Allerdings die Register der FPU sind 80Bit groß. Also so groß wie ein Extended. Und wenn ich mich gerade nicht vertue, dann werden Singles und Double innerhalb der FPU aber wieder in extended umgewandelt und es wird auch mit extended gerechnen. Also für die FPU sollte das keinen Unterschied machen. ABER es ist nicht generell so. Die Grafikkarten GPU arbeitet intern auch nur mit einfacher genauigkeit. Also Singles. Teilweise können sie auch Doubles verarbeiten aber dann müssen die Operationen aufgesplittet werden. Dadurch müssen deutlich mehr Rechenoperationen durchgeführt werden. Und das kostet dann wiederrum reichlich Zeit.

Man könnte jetzt auch auf die Idee kommen, dass man alle Werte in Extended abspeichert und bei der Übergabe an OpenGL in Single umwandelt. Nur dann sollte man bedenken, dass die Extendedwerte auch erst mal nach Single konvertiert werden müssen. Dazu muss auch wieder die FPU bemührt werden. Was vermutlich unnötig Zeit frisst. In den allermeisten Fällen wird die Genauigkeit von Singles aber auch ausreichen. Außerdem muss man auch 10 anstelle von 4 Bytes mit sich rumschleppen

PS: Bei Integer auf der CPU ist das im übrigen ähnlich wie Doubles auf der GPU. Also genau genommen nur Int64. Da die CPU Register (eines 32 Bit Systems) nur 32Bit groß sind müssen Int64 immer zerstückelt und seperat berechnet werden. Auf 64 Bit Systemen sieht das aber anders aus. Dort haben die Register 64 Bit größe. Bevor missverständnisse entstehen. Eine 64Bit CPU reicht dafür aber nicht aus. Man benötigt noch ein 64Bit Betriebssystem was die CPU in den entsprechenden Modus versetzt. 64Bit CPU und 32Bit Betriebssystem arbeiten auch nur in 32Bit.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 11 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 7 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.009s | 15 Queries | GZIP : On ]