Autor Thema: 64Bit Integer mit FPU oder 32Bit CPU  (Gelesen 6365 mal)

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« am: 27. April 2008, 00:27 »
Hallo, ich möchte auf meiner 32Bit CPU gerne mit 64Bit Integers arbeiten (in Assembler). Daher bin ich am Überlegen, was besser geeignet ist:
Alle Rechenoperationen zweistufig mit 32Bit Registern durchzuführen oder die 64Bit Integer für die Berechnung in die FPU zu laden.
Die Integer werden zwar in die Floating-Point Register der FPU geladen, können dort aber ohne Genauigkeitsverluste für Berechnungen benutzt werden und lassen sich ohne Probleme danach wieder als Integer auslesen (ohne manuelle Konvertierung).

Hat da einer von euch schon mal Erfahrungen mit gesammelt und kann mir eventuell einen Tipp geben, was schneller geht?

Korona

  • Beiträge: 94
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 27. April 2008, 10:29 »
Die FPU unterstützt keine Integer Operationen. Beim Laden in die FPU werden sie in Floating Point umgewandelt und es gibt zwangsläufig Genauigkeitsverluste. (unsigned 12345678 wird zu sowas wie: +1,2345 * 10^7)

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #2 am: 27. April 2008, 12:28 »
Ich weiß, dass sie umgewandelt werden (habe ich auch in meiner Frage geschrieben), aber sie werden in einen interne 80-Bit Float umgewandelt, und dieser kann einen 64-Bit Integer ohne Verlust speichern, da Bits 0-63 die Mantisse sind, Bits 64-78 der Exponent und Bit 79 das Vorzeichen. Durch das setzen des RC (Rounding Control) Flags auf 2 (chop) schneidet die FPU die Ergebnissen, die über den darstellbaren Bereich hinausgehen würden, ab, so dass kein Exponent benötigt wird.
Es lässt sich so also eine Integer-Arithmetik abbilden!

PS: Und mit den Befehlen FILD (Load Integer) lässt sich ein 16, 32 oder 64-Bit Integer aus dem Speicher in die FPU laden und mit FISTP (Store Integer and Pop) lässt sich ein 16, 32 oder 64-Bit Integer in den Speicher zurück schreiben (und vom Registerstack popen).
« Letzte Änderung: 27. April 2008, 12:37 von [MM] »

Korona

  • Beiträge: 94
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 27. April 2008, 14:35 »
Achso, stimmt, ich hatte garnicht an 80 Bit Floats, sondern nur an 64 Bit Floats gedacht.

In dem Fall kannst du natürlich die FPU nutzen. Mit der Nutzung der FPU für Integer Operationen habe ich keine Erfahrung, du kannst es ja mal ausprobieren, ich würde aber vermuten, dass Integer Operationen schneller sind. Andererseits hat die FPU fdiv Operationen (die allerdings auch sehr langsam sind), die man sonst aufwendig nachbilden muss. Mich würde mal interessieren, was da bessere Performance bietet. GCC verwendet auf jeden Fall Integer Register.

Eventuell ist MMX/SSE auch noch eine Alternative, ich bin mir aber nicht sicher, ob es auf Skalaren operierende Divisions-Instruktionen gibt.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 27. April 2008, 22:34 »
Hiho,

wenn du die Zahlen von den General Purpose Registers in die FPU lädst, dann musst du das über den Speicher machen. Wenn ich raten soll, ist das langsamer als zum Beispiel eine einfach ADD/ADC-Folge für eine 64 Bit Addition. Bei der Multiplikation von großen Zahlen (beide größer als 2^32) geht dir mit der FPU auch Genauigkeit verloren (dafür geht das mit Integern gar nicht in einem Schritt), und wenn du Division mit Rest brauchst, dann kommst du an den Rest nur über Umwege.

Speicherzugriffe wären bei diesen 64 Bit Zahlen außerdem auch 50% langsamer als bei der klassischen Variante, denn wenn du 80 Bit Floats im Speicher ablegst, solltest du diese erstmal auf 32 Bit ausrichten, denn intern werden immer 32 Bit Words geladen. Also 12 Bytes pro 64 Bit Zahl. (In den 2 ungenutzen Bytes kannste ja deine Einkaufsliste speichern. Als Assemblerprogrammier darfste kein Bit verkommen lassen. ;)) Wenn du feststellen willst, ob es einen Overflow (im Sinne von Ergebnis ist größer als 2^64) gab, dann musst du aufwändig den Exponenten untersuchen. Wenn du mit vorzeichenbehafteten Zahlen arbeitest, hast du außerdem effektiv 65 Bit Zahlen, weil das Vorzeichenbit nicht in der Mantisse enthalten ist.

Diese Punkte sind mir spontan eingefallen. Vielleicht trifft nicht alles für den Einsatzzweck, der dir im Sinn steht, zu, aber da der anscheinend geheim ist, kann ich nur raten. Für mich sprechen diese Argumente deutlich gegen den Einsatz dieser Methode zum Zwecke der Geschwindigkeitssteigerung. Aber mit Argumenten haben wir Assemblerprogrammier es nicht so ^^

Wenn dich obiges trotzdem nicht abhält, dann kannst du dir natürlich erstmal die Manuals von Intel und AMD, sowie deren Optimizations Guides reinpfeifen und diese Seite hier angucken: http://www.agner.org/optimize/ Deine Erfahrungen darfste dann im Wiki festhalten ;)
Dieser Text wird unter jedem Beitrag angezeigt.

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #5 am: 28. April 2008, 14:59 »
Hallo.

Zitat
Vielleicht trifft nicht alles für den Einsatzzweck, der dir im Sinn steht, zu, aber da der anscheinend geheim ist, kann ich nur raten.
Geheim ist er nicht, ich will nur 64-Bit Operationen mit 386er Befehlen durchführen, und wollte mal gucken, ob das nicht auch elegant mit der FPU geht. Aber um keine Fragen offen zu lassen: Das ganze benötige ich für einen neuen Compiler, den ich grad bastle.

Daher will ich meine 64-Bit auch nicht aus einem GPR laden, sondern immer aus dem Speicher, weswegen das kein Nachteil wäre.
Und die Manuals von Intel kenne ich bereits ;-)

Ich hab mal fix ein Beispielprogramm gebastelt, was leider beweist, dass es nicht funktioniert, wie ich mir das gewünscht hätte:
fnstcw [20]
mov byte ptr [21],0F ; Control Word auf Truncate und 64-Bit Precision setzen
fldcw [20]
fild qword ptr [0] ; Laden von 123456789ABCDEF1
fild qword ptr [8] ; Laden von 11
fmul st,st(1)
fistp qword ptr [10] ; Speichert 8000000000000000

Das Ergebnis der Multiplikation ist danach 8000000000000000, eine echte 64-Bit Multiplikation hätte natürlich 3579BE02468ACE01 geliefert.
So wie es aussieht hatte ich das Truncate/Chop-Flag falsch verstanden. Die Rundung erfolgt nicht bei der Berechnung, sondern erst beim zurück-Konvertieren in einen Integer...

Außerdem sind Shift und andere Bit-Operationen mit der FPU nicht möglich, so dass kein Weg daran vorbei führt 64-Bit Berechnungen mit 2 32-Bit Registern zu machen (auf einem 386).

PS: Es werden übrigens keine Bits verschwendet, da man keine 80Bit-Floats im Speicher ablegt (80 Bit ist nur die Registerbreite die intern in der FPU verwendet wird), sondern reine 64-Bit Integer, aber das sollte auch aus meinem Programm ersichtlich sein ;-).
« Letzte Änderung: 28. April 2008, 15:02 von [MM] »

 

Einloggen