2012-12-07 16 views
16

Ho visto questo post su SO che contiene codice C per ottenere le ultime CPU conteggio di ciclo:Ottieni il conteggio del ciclo della CPU?

CPU Cycle count based profiling in C/C++ Linux x86_64

C'è un modo per utilizzare questo codice in C++ (finestre e le soluzioni Linux benvenuto)? Sebbene scritto in C (e C essendo un sottoinsieme di C++) non sono sicuro se questo codice funzionerebbe in un progetto C++ e, in caso contrario, come tradurlo?

Sto usando x86-64

EDIT2:

Trovato questa funzione ma non può ottenere VS2010 di riconoscere l'assemblatore. Devo includere qualcosa? (Credo di avere scambiare uint64_t-long long per windows ....?)

static inline uint64_t get_cycles() 
{ 
    uint64_t t; 
    __asm volatile ("rdtsc" : "=A"(t)); 
    return t; 
} 

Edit3:

Da codice di cui sopra ottengo l'errore:

"error C2400: inline assembler syntax error in 'opcode'; found 'data type'"

Qualcuno potrebbe aiutare ?

+1

"C++ è un sottoinsieme di C" - intendevi il contrario? – Mysticial

+0

@Mysticial yup:) - modificato – user997112

+0

Visual Studio non supporta l'assembly su x86-64. –

risposta

41

Tirato direttamente di uno dei miei progetti:

#include <stdint.h> 

// Windows 
#ifdef _WIN32 

#include <intrin.h> 
uint64_t rdtsc(){ 
    return __rdtsc(); 
} 

// Linux/GCC 
#else 

uint64_t rdtsc(){ 
    unsigned int lo,hi; 
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 
    return ((uint64_t)hi << 32) | lo; 
} 

#endif 
+0

È un bel modo per impacchettarlo. –

+0

Grazie Mystical – user997112

+8

FWIW, gcc 4.5 e successivi includono __rdtsc() - #include scaricarlo. L'intestazione include anche molti altri elementi intrinseci di Intel che si trovano in Microsoft e di solito vengono inclusi per impostazione predefinita in questi giorni quando si include la maggior parte degli header SIMD - emmintrin.h, xmmintrin.h, ecc. – jstine

5

Per Windows, Visual Studio fornisce un comodo "compilatore intrinseca" (cioè una funzione speciale, che il compilatore capisce) che esegue l'istruzione RDTSC per voi e vi restituisce il risultato:

unsigned __int64 __rdtsc(void); 
6

VC++ utilizza una sintassi completamente diversa per l'assembly inline, ma solo nelle versioni a 32 bit. Il compilatore a 64 bit non supporta affatto l'assembly inline.

In questo caso, probabilmente lo è anche - rdtsc ha (almeno) due problemi principali quando si tratta di sequenze di codice di temporizzazione. Innanzitutto (come la maggior parte delle istruzioni) può essere eseguito fuori ordine, quindi se stai provando a cronometrare una breve sequenza di codice, lo rdtsc prima e dopo quel codice potrebbero essere entrambi eseguiti prima di esso, o entrambi dopo di esso, o cosa hanno tu (sono abbastanza sicuro che i due saranno sempre eseguiti in ordine l'uno rispetto all'altro, quindi almeno la differenza non sarà mai negativa).

In secondo luogo, su un sistema multi-core (o multiprocessore), un rdtsc può essere eseguito su un core/processore e l'altro su un core/processore diverso. In tal caso, un risultato negativo è interamente possibile.

In generale, se si desidera un timer preciso in Windows, sarà meglio utilizzare QueryPerformanceCounter.

Se si vuole davvero utilizzare rdtsc, credo che lo si dovrà fare in un modulo separato scritto interamente in linguaggio assembly (o utilizzare un compilatore intrinseco), quindi collegato con il C o C++. Non ho mai scritto che il codice per la modalità a 64 bit, ma in modalità a 32 bit è simile a questa:

xor eax, eax 
    cpuid 
    xor eax, eax 
    cpuid 
    xor eax, eax 
    cpuid 
    rdtsc 
    ; save eax, edx 

    ; code you're going to time goes here 

    xor eax, eax 
    cpuid 
    rdtsc 

So che sembra strano, ma in realtà è giusto. Esegui CPUID perché è un'istruzione serializzante (non può essere eseguita fuori servizio) ed è disponibile in modalità utente.Lo si esegue tre volte prima di iniziare il cronometraggio perché Intel documenta il fatto che la prima esecuzione può/verrà eseguita a una velocità diversa dalla seconda (e ciò che raccomandano è tre, quindi tre è).

Quindi si esegue il codice sotto test, un'altra cpuid per forzare la serializzazione e l'ultimo rdtsc per ottenere il tempo dopo il completamento del codice.

Insieme a ciò, si desidera utilizzare qualsiasi mezzo che il sistema operativo fornisce per forzare il tutto su un unico processo/core. Nella maggior parte dei casi, si desidera forzare l'allineamento del codice: le modifiche nell'allineamento possono portare a differenze abbastanza sostanziali in termini di spee di esecuzione.

Infine, si desidera eseguirlo un numero di volte - ed è sempre possibile che venga interrotto nel mezzo di alcune cose (ad esempio un interruttore di attività), quindi è necessario essere preparati per la possibilità di un'esecuzione prendendo un po 'più a lungo del resto - ad esempio, 5 piste che richiedono ~ 40-43 cicli di clock a testa, e un sesto che richiede più di 100 cicli di clock. Chiaramente, in quest'ultimo caso, butti fuori l'outlier - non è dal tuo codice.

Riepilogo: la gestione dell'esecuzione dell'istruzione rdtsc è (quasi) l'ultima delle tue preoccupazioni. C'è ancora un po 'di tempo che lo ha bisogno di fare prima di poter ottenere risultati da rdtsc che in realtà significheranno qualsiasi cosa.

+0

Sono abbastanza sicuro quando stavo facendo ricerche, ho trovato documentazione che 'QueryPerformanceCounter' (che è un velo sottile su' rdtsc') soffre dello stesso problema che hai identificato su sistemi multicore/multiprocessore. Ma penso di aver trovato anche documentazione che questo problema era un problema reale nei primi sistemi perché la maggior parte dei BIOS non tentava neppure di sincronizzare i contatori sui diversi core, ma molti BIOS più recenti (forse senza contare i BIOS economici della macchina spazzatura) lo fanno sforzo, in modo che possano essere fuori solo da pochi conteggi ora. – phonetagger

+0

.... Ma per evitare del tutto questa possibilità, è possibile impostare la maschera di affinità del processore di un thread in modo che venga eseguita solo su un singolo core, eliminando completamente questo problema. (che vedo anche menzionato) – phonetagger

+0

QPC può essere, ma non necessariamente, un velo sottile su rdtsc. Almeno in una volta, il kernel a processore singolo usava rdtsc, ma il kernel multiprocessore usava invece il chip di clock della 1.024 MHz della scheda madre (esattamente per le ragioni citate). –

Problemi correlati