Operator schneller als Inline Assembler



  • Registrierter Troll schrieb:

    Das Thema hat wenig mit C++ zu tun und wäre im Assembler-Forum besser aufgehoben.

    Die Fähigkeiten der C++-Compiler auszuloten ist 100% on-topic.



  • Ich kann mir vorstellen, dass der Kompiler bei der Standardlösung die Methode inlined. Bei der Lösung mit Assembler ist diese Optimierung sicherlich schwieriger. Manche Kompiler schalten bei Inline-Assembler viele Optimierungen ab. Das ist auch eigentlich richtig. Denn, wenn ich Inline-Assembler verwende, sage ich ja explizit, dass ich es besser kann als der Kompiler.



  • Ich habe die Ticks gezählt. Ich habe den Test mehrfach wiederholt. Meine Version braucht bei mir ca. 2400 Ticks, der Operator 2270(Als debug kompiliert, sonst Operator 0(wird wahrscheinlich wegoptimiert), Inline 24).

    #include "stdafx.h"
    #include <iostream>
    #include <windows.h>
    
    using namespace std; 
    
    int _add(int i, int j);
    int _add2(int i, int j);
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    int c1 = GetTickCount();
    for(int i = 0;i < 100000000; i++)
    {
     	int z = _add(100,100);
    }
    c1 = GetTickCount() - c1;
    
    cout << c1 << endl;
    c1 = GetTickCount();
    for(int i = 0;i < 100000000; i++)
    {
    	int z = _add2(100,100);
    }
    c1 = GetTickCount() - c1;
    
    cout << c1 << endl;
    cin >> c1;
    	return 0;
    }
    
    int _add(int i, int j)
    {
    __asm
    {
    	mov eax, i
    	add eax, j
    }
    }
    
    int _add2(int i, int j)
    {
    return i + j;
    }
    

    Gruß pdelvo



  • volkard schrieb:

    Registrierter Troll schrieb:

    Das Thema hat wenig mit C++ zu tun und wäre im Assembler-Forum besser aufgehoben.

    Die Fähigkeiten der C++-Compiler auszuloten ist 100% on-topic.

    Welchen Code ein x-beliebiger Compiler auf einer x-beliebigen Plattform erzeugt und wie schnell oder langsam der erzeugte Code ist, scheint mir persönlich wenig mit C++ im Sinne dieses Subforums zu tun haben. Genauso wenig wie x86-Assemblercode hier on-topic ist. Dafür gibt es die Compiler- und Assembler-Subforen, die imho besser passen.

    Forenbeschreibung schrieb:

    Fragen zu bestimmten Funktionen und Abläufen in C++ (nach dem ISO-Standard)

    ...aber du bist der Moderator, nicht ich.



  • Jetzt noch den Assembler-Code anzeigen.
    Mit Alt+F8 oder so.



  • pdelvo schrieb:

    Ich habe die Ticks gezählt. Ich habe den Test mehrfach wiederholt. Meine Version braucht bei mir ca. 2400 Ticks, der Operator 2270(Als debug kompiliert, sonst Operator 0(wird wahrscheinlich wegoptimiert)

    Debug sagt wenig aus. mach ne Summe aus der Zuweisung und gibd as Ergebnis am Ende aus. Dann kann er das auch im Release nicht wegoptimieren.



  • 01271B50  push        ebp  
    01271B51  mov         ebp,esp 
    01271B53  sub         esp,0C0h 
    01271B59  push        ebx  
    01271B5A  push        esi  
    01271B5B  push        edi  
    01271B5C  lea         edi,[ebp-0C0h] 
    01271B62  mov         ecx,30h 
    01271B67  mov         eax,0CCCCCCCCh 
    01271B6C  rep stos    dword ptr es:[edi] 
    01271B6E  mov         eax,dword ptr [i] 
    01271B71  add         eax,dword ptr [j]
    

    Er ist bei beiden Versionen vollkommen identisch.
    Schlimm ist das zwar nicht, aber es verwundert mich.

    Menu Debuggen/Fenster/Disassembly

    Gruß pdelvo



  • Registrierter Troll schrieb:

    Welchen Code ein x-beliebiger Compiler auf einer x-beliebigen Plattform erzeugt und wie schnell oder langsam der erzeugte Code ist, scheint mir persönlich wenig mit C++ im Sinne dieses Subforums zu tun haben. Genauso wenig wie x86-Assemblercode hier on-topic ist. Dafür gibt es die Compiler- und Assembler-Subforen, die imho besser passen.

    Ich erwarte exemplarische Ergebnisse, die im Prinzip auf alle Plattformen und alle aktuellen Compiler übertragbar sind. Ob der Compiler aus dem + ein ADD oder LEA macht, ist völlig egal. Wichtig sind nur die übertragbaren Sachen, zum Beispiel, daß man nie nie nie seine Summen in Zeitmessprogrammen wegwerfen darf, sondern sie ausgeben muß, weil sonst der Compiler ganze Blöcke wegoptimieren kann. Und daß man die Summen nicht zu einfach berechnen darf, also daß der Compiler statt wiederholtem + ein * machen kann. Und daß der Einsatz von Inline-Assembler das Programm potenziell verlangsamt, weil man den Compiler einschränkt.



  • pdelvo schrieb:

    Er ist bei beiden Versionen vollkommen identisch.
    Schlimm ist das zwar nicht, aber es verwundert mich.

    Das ist schlimm! Ich würde an Deiner Stelle jetzt panisch im Kreis laufen. Was hast Du gemessen? Solange der Messfehler nicht geklärt ist, kannst Du nie wieder etwas messen und dem Ergebnis glauben.



  • Ich habe die Ticks, die zwichen der Messung vergangen sind gemessen. Gibt es noch eine bessere Methode?

    Gruß pdelvo



  • volkard schrieb:

    Jetzt noch den Assembler-Code anzeigen.
    Mit Alt+F8 oder so.

    edit: es ist btw alt+8 ^^

    aber den der release-version ^^

    ich habs ma selbst gemacht mit msvc im release-mode. er übergibt die fkt-parameter nicht direkt in registern, sondern über den stack... ich hab aber auf die schnelle keine Calling-Convention gefunden, die das behebt... bin mir auch nicht ganz sicher, warum er 2 parameter per stack übergibt und nicht über register wie gesagt: asm deaktiviert eben viele optimierungen...

    __declspec(noinline) ist wie immer dazu da, um eine bessere Übersicht zu haben, weil er die Fkt nicht inlined (imho MSVC spezifisch)

    __declspec(noinline) int _add2(int i, int j)
    {
    return i + j;
    001B1010  add         eax,ecx 
    }
    
    __declspec(noinline) int _add(int i, int j)
    {
    __asm
    {
        mov eax, i
    001B1000  mov         eax,dword ptr [esp+4] 
        add eax, j
    001B1004  add         eax,dword ptr [esp+8] 
    }
    

    bb



  • unskilled schrieb:

    ich hab aber auf die schnelle keine Calling-Convention gefunden, die das behebt... bin mir auch nicht ganz sicher, warum er 2 parameter per stack übergibt und nicht über register wie gesagt: asm deaktiviert eben viele optimierungen...

    __fastcall



  • volkard schrieb:

    unskilled schrieb:

    ich hab aber auf die schnelle keine Calling-Convention gefunden, die das behebt... bin mir auch nicht ganz sicher, warum er 2 parameter per stack übergibt und nicht über register wie gesagt: asm deaktiviert eben viele optimierungen...

    __fastcall

    hatte ich probiert, aber machte es nur noch schlimmer:

    __declspec(noinline) int __fastcall _add(int i, int j)
    {
    00021000  sub         esp,8 
    00021003  mov         dword ptr [esp],edx 
    00021006  mov         dword ptr [esp+4],ecx 
    __asm
    {
        mov eax, i
    0002100A  mov         eax,dword ptr [esp+4] 
        add eax, j
    0002100E  add         eax,dword ptr [esp] 
    }
    }
    

    bb



  • pdelvo schrieb:

    Ich habe die Ticks, die zwichen der Messung vergangen sind gemessen. Gibt es noch eine bessere Methode?

    Mein erprobtes Schema: 1000 kleine Messungen machen und die nehmen, die am wenigsten Zeit brauchte. Immer, wenn in der Schleife eine bessere Zeit gefunden wurde, den Zähler wieder auf 1000 stellen.
    Damit kriegst Du gerne fünf korrekte Stellen.


Anmelden zum Antworten