Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   [ASM] Power-Geheimnisse (https://www.delphipraxis.net/98074-%5Basm%5D-power-geheimnisse.html)

Khabarakh 21. Aug 2007 01:17


[ASM] Power-Geheimnisse
 
Eigentlich wollte ich meine Kiste vor dreieinhalb Stunden abstürzen lassen herunterfahren, doch angeregt hiervon wollte ich mir einmal kurz die Power-Implementierung der RTL anschauen. Bei "kurz" ist es zwar nicht ganz geblieben, aber ich darf nun behaupten, den Code verstanden zu haben; mit der Zeit bin ich ebenfalls sehr zufrieden, denn es gab da ein kleines Problem: Ich beherrsche kein Wort Assembler :mrgreen: [1] . Ausgestattet allein mit dem Verständnis des abstrakten Stack-Typs (soll heißen: Push und Pop :stupid: ) und einer Webseite über FPU-Instruktionen habe ich mich also debuggend und kommentierend durch den Asm-Code gequält. Das Resultat sieht so aus [2]:
Delphi-Quellcode:
function Power(const Base, Exponent: Extended): Extended;
const
  Max : Double = MaxInt;
var
  IntExp : Integer;
asm
  // Kommentare stellen den FPU-Stack dar: // ST0, ST1
  fld    Exponent // b
  [...]//schnipp
  fld    Base // a, b
  [...]// schnipp
  fldln2  // ln 2, a, b
  fxch   // a, ln 2, b
  fyl2x  // ln 2 * ld a, b
  fxch   // b, ln 2 * ld a
  fmulp  st(1), st // ln 2 * ld a * b
  fldl2e // ld e, ln 2 * ld a * b
  fmulp  st(1), st // ld e * ln 2 * ld a * b = ld a * b = x
  fld    st(0) // x, x
  frndint // Round(x), x
  fsub   st(1), st // Round(x), x - Round(x) // sagen wir einfach Int(x) und Frac(x), auch wenn wir ja auch aufgerundet haben könnten // siehe auch [url]http://t-a-w.blogspot.com/2006/06/docking-assembly.html[/url]
  fxch   st(1) // Frac(x), Int(x)
  f2xm1   // 2^Frac(x) - 1, x
  fld1    // 1, 2^Frac(x) - 1, x
  faddp  st(1), st // 1 + 2^Frac(x)-1 = 2^Frac(x)
  fscale // 2^Frac(x) * 2^Int(x) = 2^z = 2^(ld a * b) = a^b
end;
Das Ergebnis stimmt, trotzdem müsst ihr mir noch beim Füllen zweier Verständnislücken assistieren: Denn die Funktion zweier Teile habe ich zwar begriffen, doch ... kommen mir sie einfach überflüssig vor :freak: .

I
Delphi-Quellcode:
  fxch   // b, ln 2 * ld a
  fmulp  st(1), st // ln 2 * ld a * b
Bei der Kommentierung dieses Codestücks wurde ich vage an ein Ding namens Kommutativgesetz erinnert... Kurz und knapp: ich sehe keinen Grund, weshalb man die Register vertauschen sollte, wenn man danach sowieso beide in einer Multiplikation kombiniert.

II
Delphi-Quellcode:
fmulp  st(1), st // ld e * ln 2 * ld a * b = ld a * b = x
Nicht gerade das bekannteste Logarithmusgesetz, aber ihr dürft es gern selbst nachrechnen: log_a(b) * log_b(a) = 1, womit die Umformung im Kommentar gültig ist. Da der Code "einfach nur" 2^(ld a * b) berechnet, muss an dieser Stelle natürlich auch ld a * b herauskommen, das wussten also auch die Entwickler des Codes - doch wozu dann überhaupt das ld e und ln 2, wozu so kompliziert?

Fazit:
Nun denke ich natürlich in beiden Punkten zuerst, dass dies einfach zwei Tricks in diesem Machwerk hochperformaten Assemblers sei, doch irgendetwas lässt mich daran zweifeln... ist wohl die Tatsache, dass meine eigengebaute Power-Routine, die diese zwei Punkte verbessert, 2% schneller ist als das Original :zwinker: .

Hier noch der verbesserte Abschnitt:
Delphi-Quellcode:
  fld1 {   fldln2   }
  fxch   // a, >1<, b
  fyl2x  // >1< * ld a, b
  {   fxch   // b, ln 2 * ld a  }
  fmulp  st(1), st // ld a * b = x
  {   fldl2e // ld e, ln 2 * ld a * b
      fmulp  st(1), st // ld e * ln 2 * ld a * b = ld a * b  }
Mal sehen, ob der Titel ansprechend genug formuliert ist :mrgreen: .

[1]Ich beherrsche wahrscheinlich sogar CIL besser als Assembler - allein durch den Reflector :duck: .
[2]Hoffe mal, das Ausmaß der zitierten Code-Abschnitte liegt noch im (Copyright-)Rahmen.

Nacht ihr :hi: .

sirius 21. Aug 2007 08:38

Re: [ASM] Power-Geheimnisse
 
Wo hast du denn das her?

Das ist doch viel kürzer (obs schneller ist, weis ich nicht):
Delphi-Quellcode:
function power(x,y:extended):extended;
asm
  FLD TBYTE ptr y
  FLD TBYTE ptr x
  FYL2X
  FLD st
  FRNDINT
  FSUB st(1),st
  FXCH
  F2XM1
  FLD1
  FADD
  FSCALE
  FXCH
  FSTP st
end;
Es basiert eben direkt auf 2^(y*lb(x)). Jetzt ist die Frage, wie lange FYL2X, F2XM1 und FSCALE brauchen.
Es sollte aber schneller sein als exp(y*ln(x)) direkt aus der Unit math.

Zu Frage I:
Schau dir mal an wozu das P hinter FMUL da ist :zwinker:

Zu Frage II:
Weis auch nicht, siehe oben.

Khabarakh 21. Aug 2007 11:58

Re: [ASM] Power-Geheimnisse
 
Zitat:

Zitat von sirius
Wo hast du denn das her?

Aus einer Unit namens "Mathe" oder so :mrgreen: ? Turbo Delphi, sollte sich das etwas geändert haben.
Zitat:

Das ist doch viel kürzer (obs schneller ist, weis ich nicht):
Delphi-Quellcode:
function power(x,y:extended):extended;
asm
  FLD TBYTE ptr y
  FLD TBYTE ptr x
  FYL2X

Stimmt, das FLD1 und FMULP hätte ich mir an dieser Stelle sparen und FYL2X (ich liebe diesen Namen) gleich mit x und y füttern können. Mathematik nach 10 Uhr eben :zwinker: . Mit diesen zwei Zeilen weniger wären wir schon bei 4% Vorsprung gegenüber dem Original :D .
Zitat:

Es basiert eben direkt auf 2^(y*lb(x)). Jetzt ist die Frage, wie lange FYL2X, F2XM1 und FSCALE brauchen.
Es sollte aber schneller sein als exp(y*ln(x)) direkt aus der Unit math.
Jupp, auf Asm-Ebene wird das sicherlich stimmen. Aber im jenem anderen Thread suchten wir ja nach einem möglichst kurzen Power-Hochsprachen-Ersatz ohne Math-Unit. Denn sobald ich dort ein wenig mehr optimieren will, klaue ich doch gleich den Power-Code (nächste Wortspiel :stupid: ) aus Math mit seinen weiteren Optimierungen (hätte nicht gedacht, dass Power wirklich eine Schleife benutzt).
Zitat:

Zu Frage I:
Schau dir mal an wozu das P hinter FMUL da ist :zwinker:
Zitat:

Multiply ST(i) by ST(0), store result in ST(i), and pop the register stack
Soll heißen, es passiert folgendes:
Delphi-Quellcode:
// ST0, ST1
// a, b
fxch
// b, a
fmulp st(1), st
// b, b * a
// pop ->
// b * a,
Ohne fxch gelange ich am Ende zu ST0 = a * b, wo ist also der Unterschied :gruebel: ?

sirius 21. Aug 2007 12:12

Re: [ASM] Power-Geheimnisse
 
Zitat:

Zitat von Khabarakh
Soll heißen, es passiert folgendes:
Delphi-Quellcode:
// ST0, ST1
// a, b
fxch
// b, a
fmulp st(1), st
// b, b * a
// pop ->
// b * a,
Ohne fxch gelange ich am Ende zu ST0 = a * b, wo ist also der Unterschied :gruebel: ?

Ja mit st(1) macht es nicht wirklich Sinn :gruebel:

Zitat:

Aus einer Unit namens "Mathe" oder so ? Turbo Delphi, sollte sich das etwas geändert haben.
Aha, da haben sie also von D7 auf D2006 was geändert.

OlafSt 21. Aug 2007 12:38

Re: [ASM] Power-Geheimnisse
 
Auch wenn es komisch und unsinnig aussieht - das macht Sinn, dieses FXCHG und dann FMULP st(1),st

Überlege mal, was wäre, wenn das FXCHG nicht da wäre und was man alles machen müßte, um am Ende das gleiche Ergebnis und einen sauberen FPU-Stack zu haben...

sirius 21. Aug 2007 12:57

Re: [ASM] Power-Geheimnisse
 
Zitat:

Zitat von OlafSt
Auch wenn es komisch und unsinnig aussieht - das macht Sinn, dieses FXCHG und dann FMULP st(1),st

Überlege mal, was wäre, wenn das FXCHG nicht da wäre und was man alles machen müßte, um am Ende das gleiche Ergebnis und einen sauberen FPU-Stack zu haben...

Man macht eben nur FMULP ohne FXCH und kommt aufs selbe Ergebnis in der FPU :|


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:01 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