Frage

#include <stdio.h>
static inline unsigned long long tick() 
{
        unsigned long long d;
        __asm__ __volatile__ ("rdtsc" : "=A" (d) );
        return d;
}

int main()
{
        long long res;
        res=tick();

        res=tick()-res;
        printf("%d",res);
        return 0;
}

Ich habe diesen Code mit GCC mit -O0 -O1 -O2 -O3 -Optimierungen zusammengestellt. Und ich bekomme immer 2000-2500 Zyklen. Kann jemand den Grund für diese Ausgabe erklären? Wie verbringt man diese Zyklen?

Die erste Funktion "Tick" ist falsch. Dies ist richtig.

Eine andere Version der Funktion "Tick"

static __inline__ unsigned long long tick()
{
  unsigned hi, lo;
  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
  return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}

Dies ist Assemblercode für -o3

 .file  "rdtsc.c"
.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    %ecx, -16(%ebp)
    movl    %ebx, -12(%ebp)
    movl    %esi, -8(%ebp)
    movl    %edi, -4(%ebp)
#APP
# 6 "rdtsc.c" 1
    rdtsc
# 0 "" 2
#NO_APP
    movl    %edx, %edi
    movl    %eax, %esi
#APP
# 6 "rdtsc.c" 1
    rdtsc
# 0 "" 2
#NO_APP
    movl    %eax, %ecx
    movl    %edx, %ebx
    subl    %esi, %ecx
    sbbl    %edi, %ebx
    movl    %ecx, 4(%esp)
    movl    %ebx, 8(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    -16(%ebp), %ecx
    xorl    %eax, %eax
    movl    -12(%ebp), %ebx
    movl    -8(%ebp), %esi
    movl    -4(%ebp), %edi
    movl    %ebp, %esp
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
    .ident  "GCC: (Debian 4.3.2-1.1) 4.3.2"
    .section    .note.GNU-stack,"",@progbits

Dies ist CPU

processor   : 0
vendor_id   : GenuineIntel
cpu family  : 15
model       : 4
model name  : Intel(R) Xeon(TM) CPU 3.00GHz
stepping    : 3
cpu MHz     : 3000.105
cache size  : 2048 KB
fdiv_bug    : no
hlt_bug     : no
f00f_bug    : no
coma_bug    : no
fpu     : yes
fpu_exception   : yes
cpuid level : 5
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss constant_tsc up pebs bts pni
bogomips    : 6036.62
clflush size    : 64
War es hilfreich?

Lösung

Ich habe Ihren Code auf mehreren Linux -Distributionen ausprobiert, die auf verschiedenen Intel -CPUs ausgeführt werden (zugegebenermaßen alle neuer als das Pentium 4 HT 630, das Sie zu verwenden scheinen). In all diesen Tests habe ich Werte zwischen 25 und 50 Zyklen erhalten.

Meine einzige Hypothese, die mit allen Beweisen übereinstimmt, ist, dass Sie Ihr Betriebssystem in einer virtuellen Maschine und nicht auf bloßem Metall ausführen, und TSC wird virtualisiert.

Andere Tipps

Es gibt eine Reihe von Gründen, um eine große Anzahl zu erhalten:

  • Das Betriebssystem hat einen Kontextschalter durchgeführt und Ihr Prozess wurde eingeschläfert.
  • Eine Festplatte trat auf, und Ihr Prozess wurde eingeschlafen.
  • … Eine Reihe von Gründen, warum Ihr Prozess ignoriert wird.

Beachten Sie, dass rdtsc ist nicht besonders zuverlässig für das Timing ohne Arbeit, weil:

  • Prozessorgeschwindigkeiten können sich ändern, und somit ändert sich die Länge eines Zyklus (wenn in Sekunden gemessen).
  • Verschiedene Prozessoren können für den TSC für einen bestimmten Zeitraum unterschiedliche Werte für den TSC haben.

Die meisten Operatings-Systeme verfügen über eine hochpräzise Uhr oder eine Zeitvermittlung. clock_gettime Zum Beispiel auf Linux, insbesondere die monotonischen Uhren. (Verstehen Sie auch den Unterschied zwischen einer Wandverkleidung und einer monotonischen Uhr: Eine Wanduhr kann sich rückwärts bewegen-auch in UTC.) An Fenstern denke ich, dass die Empfehlung denke QueryHighPerformanceCounter. In der Regel bieten diese Uhren mehr als genug Genauigkeit für die meisten Bedürfnisse.


Wenn Sie sich die Baugruppe ansehen, sieht es so aus, als ob Sie nur 32 Bit der Antwort erhalten: Ich sehe nicht %edx danach gerettet werden rdtsc.


Wenn Sie Ihren Code ausführen, erhalte ich Timings von 120-150 NS für clock_gettime Verwendung CLOCK_MONOTONIC, und 70-90 Zyklen für RDTSC (~ 20 ns bei voller Geschwindigkeit, aber ich vermute, dass der Prozessor gesetzt wird, und das sind wirklich ungefähr 50 ns). (Auf einen LaptopDesktop (verdammt SSH, vergessen, auf welcher Maschine ich mich befand!) Das ist bei etwa einer konstanten 20% igen CPU -Verwendung) sicher, dass Ihre Maschine nicht festgefahren ist?

Es sieht so aus, als ob Ihre OS -deaktivierte Ausführung von RDTSC im Benutzerbereich deaktiviert ist. Und Ihre Anwendung muss zu Kernel und Rücken wechseln, was viele Zyklen erfordert.

Dies stammt aus dem Handbuch des Intel -Softwareentwicklers:

Im geschützten oder virtuellen 8086 -Modus schränkt das Time Stempel Disable (TSD) im Register CR4 die Verwendung der RDTSC -Anweisung wie folgt ein. Wenn das TSD -Flag klar ist, kann die RDTSC -Anweisung auf jeder Berechtigungsstufe ausgeführt werden. Wenn das Flag festgelegt ist, kann der Befehl nur auf der Privilegienstufe 0 ausgeführt werden. (Wenn der RDTSC-Befehl im Realadress-Modus immer aktiviert ist.)

Bearbeiten:

Ich erkläre den Kommentar von AIX, warum TSD hier höchstwahrscheinlich der Grund ist.

Ich kenne nur diese Möglichkeiten für ein Programm, um einen einzelnen Anweisungen länger als gewöhnlich durchzuführen:

  1. Unter einem Emulator laufen,
  2. Verwenden von selbstmodifiziertem Code,
  3. Kontextschalter,
  4. Kernelschalter.

Die ersten 2 Gründe können die Ausführung in der Regel nicht mehr als wenige hundert Zyklen verzögern. 2000-2500 Zyklen sind typischer für den Kontext-/Kernelschalter. Aber es ist praktisch unmöglich, einen Kontextschalter mehrmals an derselben Stelle zu fangen. Es sollte also Kernelschalter sein. Dies bedeutet, dass entweder das Programm unter Debugger oder RDTSC im Benutzermodus nicht zulässig ist.

Der wahrscheinlichste Grund für OS, RDTSC zu deaktivieren, kann Sicherheit sein. Es gab Versuche, RDTSC zu verwenden, um Verschlüsselungsprogramme zu knacken.

Anweisung Cache Miss? (Das ist meine Vermutung)

Auch möglicherweise,

Wechseln Sie in einem virtualisierten System zum Hypervisor? Reste von Programmbootstrap (einschließlich Netzwerkaktivität auf derselben CPU)?

An Thanatos: Bei Systemen, die neuer als 2008 sind, ist RDTSC () eine Wanduhr und variiert nicht mit Frequenzschritten.

Können Sie diesen kleinen Code ausprobieren?

int main()
{   
    long long res;

    fflush(stdout);           // chnage the exact timing of stdout, in case there is something to write in a ssh connection, together with its interrupts

    for (int pass = 0; pass < 2; pass++)
    {
    res=tick();
    res=tick()-res;
    }
    printf("%d",res);     // ignore result on first pass, display the result on second pass.
    return 0;
}

Nur eine Idee - vielleicht werden diese beiden RDTSC -Anweisungen auf verschiedenen Kernen ausgeführt? RDTSC -Werte können zwischen den Kernen geringfügig variieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top