Domanda

#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;
}

Ho compilato questo codice con gcc con -O0 -O1 -O2 -O3 ottimizzazioni. E ho sempre trovato 2000-2500 cicli. Qualcuno può spiegare il motivo di questa uscita? Come spendere questi cicli?

Primo funzione "tick" è sbagliato. Questo è giusto .

Un'altra versione della funzione "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 );
}

Questa è l'Assemblea codice per -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

Si tratta di 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
È stato utile?

Soluzione

Ho provato il codice su diverse distribuzioni Linux in esecuzione su diverse CPU Intel (certamente tutti più recente rispetto al Pentium 4 HT 630 in cui sembra essere utilizzando). In tutti questi test ho ottenuto valori compresi tra 25 e 50 cicli.

La mia unica ipotesi che è coerente con tutta l'evidenza è che si sta eseguendo il sistema operativo all'interno di una macchina virtuale, piuttosto che su metallo nudo, e TSC sta ottenendo virtualizzato.

Altri suggerimenti

Ci sono una serie di ragioni per ottenere un gran numero:

  • Il sistema operativo ha fatto un cambio di contesto, e il processo sono messo a dormire.
  • seek Si è verificato un disco e il processo sono messo a dormire.
  • ... qualsiasi di una gran numero di motivi per cui il processo potrebbe avere ignorato.

Si noti che rdtsc non è particolarmente affidabile per tempi senza lavoro, perché:

  • velocità del processore possono cambiare, e quindi, la lunghezza di un ciclo (quando misurata in secondi) cambia.
  • processori diversi possono avere valori diversi per la TSC per un dato istante di tempo.

La maggior parte dei sistemi operatings hanno un orologio ad alta precisione o il metodo di sincronizzazione. clock_gettime su Linux, ad esempio, in particolare gli orologi monotone. (Comprendere troppo la differenza tra una parete-orologio e un orologio monotona:. Un orologio da parete in grado di tornare indietro - anche in UTC) In Windows, credo che la raccomandazione è QueryHighPerformanceCounter. In genere questi orologi forniscono più che sufficiente precisione per la maggior parte delle esigenze.


Inoltre, guardando il gruppo, sembra che stai ricevendo solo 32-bit della risposta:. Non vedo %edx sempre salvato dopo rdtsc


Il funzionamento del vostro codice, ottengo tempi da 120-150 ns per clock_gettime utilizzando CLOCK_MONOTONIC, e 70-90 cicli per rdtsc (~ 20 ns a tutta velocità, ma ho il sospetto che il processore ha un clock verso il basso, e questo è realmente circa 50 ns ). (Su un laptop desktop (SSH maledettamente, ha dimenticato che la macchina ero su!), Che si trova a circa un costante utilizzo della CPU del 20%) che la macchina non è impantanato?

Sembra che il vostro sistema operativo disattivato esecuzione di RDTSC nello spazio utente. E l'applicazione dispone di passare al kernel e ritorno, che prende un sacco di cicli.

Questo è dal sviluppatori Intel Software manuale:

In modalità protetta o virtuale 8086, la disabilitazione time stamp (TSD) bandiera registro CR4 limita l'uso delle istruzioni RDTSC come segue. Quando la bandiera TSD è chiaro, l'istruzione RDTSC può essere eseguita in qualsiasi livello di privilegio; quando la bandiera è impostato, l'istruzione può essere eseguita solo a livello di privilegi 0. (quando in real-indirizzo modalità, l'istruzione RDTSC è sempre abilitato.)

Modifica

Risposta a commento di Aix, spiego, perché TSD è probabilmente il motivo per qui.

Lo so solo queste possibilità per un programma di eseguire una singola istruzione più lunga del solito:

  1. Esecuzione sotto qualche emulatore,
  2. utilizzando il codice di auto-modificato,
  3. cambio di contesto,
  4. interruttore kernel.

Per prima 2 motivi non può di solito ritardare l'esecuzione per più di qualche centinaio di cicli. 2000-2500 cicli sono più tipico per cambio di contesto / kernel. Ma è praticamente impossibile per la cattura di un cambio di contesto più volte nello stesso posto. Così dovrebbe essere l'interruttore del kernel. Il che significa che entrambi i programmi è in esecuzione in debugger o RDTSC non è consentito in modalità utente.

La ragione più probabile per OS a RDTSC disabilitare potrebbe essere la sicurezza. Ci sono stati tentativi di utilizzare RDTSC per rompere programmi di crittografia.

cache istruzioni perdere? (Questa è la mia ipotesi)

Inoltre, possibilmente,

Passa alla hypervisor in un sistema virtualizzato? Resti di programma di bootstrap (tra cui attività di rete sulla stessa CPU)?

Per Thanatos:. Sui sistemi più recenti, rispetto al 2008, rdtsc () è un orologio da parete e non varia con passi di frequenza

Si può provare questo piccolo codice?

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;
}

Solo un'idea - forse queste due istruzioni RDTSC vengono eseguiti su diversi core? valori RDTSC possono leggermente variare tra nuclei.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top