Finger weg vom xchg-Befehl...



  • Habe mal mit dem xchg-Befehl eine Schleife laufen lassen und zum Vergleich eine zweite Schleife mit lauter nop, um zu sehen, wieviele nop-Befehle ungefähr Pi mal Daumen genauso viele Takte brauchen. Hier sind die Funktionen, mit denen ich es getestet habe:

    .intel_syntax
    
    .global _getCurrentTimeStampCounter
    .global _getTimeStampCounterA
    .global _getTimeStampCounterB
    
    .equ MAX_LOOP_CNT, 1000000
    
    .section .text
    
    _getCurrentTimeStampCounter:
        rdtsc
        ret
    
    _getTimeStampCounterA:
        push ebx
    
        mov ecx, MAX_LOOP_CNT
    0:
        .rept 36
        nop
        .endr
        dec ecx
        jnz 0b
    
        cpuid           # serialize
        rdtsc
        pop ebx
        ret
    
    _getTimeStampCounterB:
        push ebx
    
        mov ecx, MAX_LOOP_CNT
    0:
        xchg eax, TmpVar
        dec ecx
        jnz 0b
    
        cpuid           # serialize
        rdtsc
        pop ebx
        ret
    
    .section .data
    
    TmpVar: .long 0
    

    Makefile und Testcode ähnlich wie hier http://www.c-plusplus.net/forum/viewtopic-var-t-is-237896.html

    Das Ergebnis: 36 nop-Befehle sind in der oberen Schleife notwendig, um ungefähr genauso viele Takte zu verbraten wie die zweite Schleife mit einem einzigen xchg-Befehl. Ein Hammer, oder? 🙂 Das nenne ich Physik, ich spüre förmlich, wie die CPU Takte verbraucht... 🙂



  • ^^xchg braucht drei takte oder so. mit der messung ist was faul. caching und pipelining machen dir da sowieso 'nen strich durch die rechnung.
    🙂



  • +fricky schrieb:

    ^^xchg braucht drei takte oder so. mit der messung ist was faul. caching und pipelining machen dir da sowieso 'nen strich durch die rechnung.

    Wieviele Takte weiss ich nicht und ist mir egal, ich sehe nur, was ich sehe 😉 Wenn jemand einen Fehler in meiner Messmethode sieht, soll sich ruhig mit der folgenden Einstellung melden:

    ⚠ Nur kein Respekt vor dem Code ! ⚠

    Wenn man übrigens xchg reg, reg macht, dann benötigt die obere Schleife nur 3 nop-Befehle, um ungefähr genauso schnell zu sein:

    .intel_syntax
    
    .global _getCurrentTimeStampCounter
    .global _getTimeStampCounterA
    .global _getTimeStampCounterB
    
    .equ MAX_LOOP_CNT, 1000000
    
    .section .text
    
    _getCurrentTimeStampCounter:
        rdtsc
        ret
    
    _getTimeStampCounterA:
        push ebx
    
        mov ecx, MAX_LOOP_CNT
    0:
        .rept 3
        nop
        .endr
        dec ecx
        jnz 0b
    
        cpuid           # serialize
        rdtsc
        pop ebx
        ret
    
    _getTimeStampCounterB:
        push ebx
    
        mov ecx, MAX_LOOP_CNT
    0:
        #xchg eax, TmpVar
        xchg eax, ebx
        dec ecx
        jnz 0b
    
        cpuid           # serialize
        rdtsc
        pop ebx
        ret
    
    .section .data
    
    TmpVar: .long 0
    


  • abc.w schrieb:

    ich sehe nur, was ich sehe...

    ...und - lass mich raten - du testest unter einem multitasking-os, ne?
    wenn ja, dann sind deine messungen ungefähr so sinnvoll, wie ein snooker-turnier auf einem schiff bei windstärke 12.

    abc.w schrieb:

    Nur kein Respekt vor dem Code

    wie meinste denn das?
    🙂



  • +fricky schrieb:

    ...und - lass mich raten - du testest unter einem multitasking-os, ne? wenn ja, dann sind deine messungen ungefähr so sinnvoll, wie ein snooker-turnier auf einem schiff bei windstärke 12.

    Ja, ich teste unter Windows XP. Aber Dein Vergleich ist übertrieben. Angemessener wäre vielleicht ein Vergleich mit einem Boxkampf mit seinen Runden und und den ganzen Pausen dazwischen...

    +fricky schrieb:

    abc.w schrieb:

    Nur kein Respekt vor dem Code

    wie meinste denn das?

    Ich meine damit z.B., über den Code schauen und dabei fest daran glauben, dass mindestens ein Fehler drin ist... 🙂



  • Was erwartest du denn? Speicherzugriffe sind nunmal teuer und du kommst ja auf die 3 Takte bei xchg reg,reg wie fricky sagte. Faktor ~10 bei (gecachtem) Speicherzugriff ist absolut realistisch.



  • Helferlein, Ein schrieb:

    Was erwartest du denn? Speicherzugriffe sind nunmal teuer und du kommst ja auf die 3 Takte bei xchg reg,reg wie fricky sagte. Faktor ~10 bei (gecachtem) Speicherzugriff ist absolut realistisch.

    Vorsicht, ich komme nicht auf 3 Takte, sondern auf 3 nops bei xchg reg, reg und auf 36 nops bei xchg reg, mem. Wieviele Takte ein nop braucht, weiss ich nicht. Wüsste auch nicht, wie man es genau messen könnte. In diesem Sinne, wie kommst Du auf Faktor 10? 😉
    Ich glaube, ich weiss jetzt, warum ein xchg reg, mem Befehl so langsam ist. In der Intel-Doku steht, dass bei diesem Befehl bei Speicheroperanden automatisch ein LOCK Mechanismus aktiv wird, unanhängig davon, ob ein LOCK Prefix da ist oder nicht. Vermutlich blockiert er dann alles. Man kann damit Semaphore-Funktionalität implementieren.



  • Das traurige ist, dass deine künstlichen Messungen dir garnicht den wahren Wert der ganzen Operationen zeigen und die Zusammenhänge erst recht nicht.
    Ein winziger loop der nur Unsinn macht nutzt so ziemlich garnichts davon aus was CPUs an Optimierungen haben.
    Dein xchg der 30 Takte zieht ist entsprechend mau, in normalen Fällen sind das eher 300.



  • abc.hirn schrieb:

    Das traurige ist, dass deine künstlichen Messungen dir garnicht den wahren Wert der ganzen Operationen zeigen und die Zusammenhänge erst recht nicht.
    Ein winziger loop der nur Unsinn macht nutzt so ziemlich garnichts davon aus was CPUs an Optimierungen haben.
    Dein xchg der 30 Takte zieht ist entsprechend mau, in normalen Fällen sind das eher 300.

    So winzig sind meine Schleifen nicht. Machen immerhin eine Million Wiederholungen. Sie machen natürlich Unsinn, wenn Du bessere Beispiele kennst, dann her mit dem Code.
    Wie kommst Du auf 30 Takte? 300 Takte? Wie gemessen?


Anmelden zum Antworten