Frage zu inline-ASM Code



  • Hi,
    ich habe mit meinen sehr beschränkten ASM-Kenntnissen versucht, ein paar simple Mathe-Funktionen zu schreiben. Allerding glaube ich, dass ich ziemlich uneffektiv gecodet habe.
    Hier mal eine Funkrion:

    inline double Round(double d)
    {
       unsigned short control;
       _asm
       {
          fstcw control
          mov ax, control
          and ax, 0xf3ff
          mov control, ax
          fldcw control
    
          fld d
          frndint
          fstp d
       }
       return d;
    }
    
    1. Das setzen des Control-Flags sieht ziemlich umständlich aus. Ist das richtig so?

    2. Nicht das es schneller wäre, aber kann man die Variable und das return iregenwie in den ASM-Code reinziehen?

    3. Ich hab mal gelesen, dass die Parameter der Funktionen schon in einem Register gespeichert sind und aus dem schneller geladen werden könnte. Stimmt das? Wie würde der Befehl 'fld d' dann aussehen?

    4. bei VC++ gibt es das Keyword __declspec(naked). Es soll dadurch (überflüssiger) Code am Anfang und Ende entfernt werden. Was muss man dann bei der Funktion ändern? Kann mir jemand ein Beipsiel geben?

    5. Muss der FPU-Stack am Ende der Funktion wieder leer sein, oder ist es egal, wenn ich ein paar Werte drin lasse?



  • Also zuallererst: What for?

    Wenn du das schnell haben willst, dann nimm entweder die Methode aus dem Intel Architecture Optimization Reference Manual oder zähle 0.5 hinzu und runde dann, ohne das CW anzugreifen.

    1. Mit naked wahrscheinlich schon
    2. floats und doubles nicht (höchstens mit __fastcall - musst halt ausprobieren)
    3. Schau dir in der C Library von Microsoft an, wie die es verwenden
    4. Ja, muss so sein wie vorher


  • Intel Architecture Optimization Reference Manual:
    Hab ich im Internet gefunden, allerdings kann ich darin nichts finden, was mir weiterhilft.

    0.5 hinzuzählen:
    Dabei gibt es ein Problem: Wenn ich eine Gerade Zahl runde z.B. 5, kommt beim abrunden 4 raus und beim aufrunden 6. Gibt es eine Lösung für dieses Problem?



  • Also ich seh da auf Seite 2-25 "Floating-point to Integer Conversion" (in der Hierarchie ist das unter "2 General Optimization Guidelines" / "Improving the Performance of Floating-point Applications")



  • Ich würde dieses "Optimieren" lassen, wenn du nicht ein grundsätzliches Verständnis von Assembler und der Maschine auf der du es einsetzt, besitzt.
    Das einzige was nachher passiert, ist dass man sich über unerklärliche Fehler wundert, die sich plötzlich während der Ausführung irgendwo im restlichen Code manifestieren.

    cya
    liquid



  • So viel optimiere ich ja nicht, sind 19 Funktionen von denen 14 3-4 Zeilen sind.
    Nur die anderen 5 machen mir Probleme.

    Wegen __fastcall & _declspec(naked): Da hab ich was im INet gefunden:

    __declspec(naked) float __fastcall Abs(float f)
    {
       __asm
       {
          fld DWORD PTR [esp+4]
          fabs
          ret 4
       }
    }
    

    Eigentlich ganz einfach, aber wie weiß der Compiler, was zurückgegeben wird?
    Für double müsste es dann so aussehen, oder?:

    __declspec(naked) double __fastcall Abs(double d)
    {
       __asm
       {
          fld QWORD PTR [esp+4]
          fabs
          ret 8
       }
    }
    

    wenn ich 2 Parameter (float) hab, kann ich dann den 2. so abrufen?:

    fld DWORD PTR [esp+8]
    

    Der Code von Intel ist zu hoch für mich.
    Ich hab keine Ahnung, wie ich ihn in meine Funktion einbauen kann.
    Kann mir da jemand weiterhelfen?



  • Diese Absolute Funktion bringts ja auch wirklich voll. Nichts für ungut, aber mit solchen "Optimierungen" erreichst du wirklich gar nichts.

    Push, Load, Compute, Return, Pop für eine Instruktion, die vielleicht nur aus einem Compute bestehen müsste. Diese 5 Zeilen ASM Funktionen haben keinen Nährwert.

    Wenn du wirklich von vielen Werten den absoluten berechnen willst, dann mach dir die Eigenarten des IEEE Formates zu nutze und benutz die Integereinheit für die Berechnung. Damit sparste dir schonmal das Laden in die FPU Register und das anschließende Zurückschreiben.

    cya
    liquid



  • Der Code für Abs() ist nicht von mir. Der ist von Intel. Dabei war der Kommentar, dass der Code schneller ist, als ein Aufruf der Standard-C-Funktion.

    Ich schreibe zur Zeit in einem kleinen Team ein Mathe-Lib, die auf 2/3D-Spieleprogrammierung spezialisiert ist. Jetzt teste ich, ob und wieviel eine ASM-Implementierung schneller ist als die Standard-Mathe-Funktionen.

    Edit: Wie würde denn die Abs()-Funktion noch schnneller sein?



  • Es muesste reichen, einfach das Vorzeichenbit auszuschalten, also mit 0x7fffffff ver-unden (fuer float). Das setzt voraus, dass das Vorzeichenbit im hoechstwertigen Bit ist (was ich zwar glaube, aber jetzt nicht nachschauen will). Was NaN und Inf betrifft, musst du selber schauen, ob da Bloedsinn rauskommt.



  • ich hab mir jetzt ein paar Tutorials angeschaut, und bin mit meinem Code schon weiter.

    Was ich aber immer weniger kapier ist, die die Parameter & Rückgabewert bei _declspec(naked) & _fastcall gespeichert wird.

    Bei den meisten Funktionen, die ich im wen gefunden wird, wird der Rückgabetyp in eax gespeichert, bei der Abs-Funktion (siehe oben) aber in st(0). WARUM?

    Wie werden die Parameter gespeichert?
    Wenn der erste Parameter ein float (4 Byte) ist, bei [esp+4].
    Wie sieht das bei double aus? Und bei einem 2. Parameter?
    Kann mir das jemand erklären oder kennt jemand ein Link?

    MSDN & google können mir leider nicht mehr weiterhelfen.


Anmelden zum Antworten