Come ottenere il conteggio del ciclo della CPU in x86_64 da C++?

Come ottenere il conteggio del ciclo della CPU in x86_64 da C++?


Ho visto questo post su SO che contiene il codice C per ottenere l'ultimo conteggio del ciclo della CPU:


Profilazione basata sul conteggio del ciclo della CPU in C/C++ Linux x86_64


C'è un modo per usare questo codice in C++ (soluzioni Windows e Linux benvenute)? Sebbene sia scritto in C (e C essendo un sottoinsieme di C++), non sono troppo 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 riesco a far riconoscere l'assembler a VS2010. Devo includere qualcosa? (Credo di dover scambiare uint64_t a long long per Windows....?)


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

EDIT3:


Dal codice sopra ottengo l'errore:



Qualcuno potrebbe aiutare per favore?


Risposte:


A partire da GCC 4.5 e versioni successive, il __rdtsc() intrinseco è ora supportato sia da MSVC che da GCC.


Ma l'inclusione necessaria è diversa:


#ifdef _WIN32
#include <intrin.h>
#else
#include <x86intrin.h>
#endif


Ecco la risposta originale prima di GCC 4.5.


Estratto direttamente da 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

Questo GNU C Extended asm dice al compilatore:



  • volatile :gli output non sono una pura funzione degli input (quindi deve essere eseguito nuovamente ogni volta, non riutilizzare un vecchio risultato).

  • "=a"(lo) e "=d"(hi) :gli operandi di uscita sono registri fissi:EAX e EDX. (vincoli macchina x86). Il rdtsc x86 l'istruzione inserisce il suo risultato a 64 bit in EDX:EAX, quindi lasciando che il compilatore scelga un output con "=r" non funzionerebbe:non c'è modo di chiedere alla CPU che il risultato vada altrove.

  • ((uint64_t)hi << 32) | lo - estende a zero entrambe le metà a 32 bit a 64 bit (perché lo e hi sono unsigned ) e spostarli logicamente + OR insieme in un'unica variabile C a 64 bit. Nel codice a 32 bit, questa è solo una reinterpretazione; i valori rimangono ancora in una coppia di registri a 32 bit. Nel codice a 64 bit in genere ottieni un'effettiva istruzione shift + OR asm, a meno che la metà alta non ottimizzi via.


(nota del redattore:questo potrebbe essere probabilmente più efficiente se utilizzassi unsigned long invece di unsigned int . Quindi il compilatore saprebbe che lo era già esteso a zero in RAX. Non saprebbe che la metà superiore è zero, quindi | e + sono equivalenti se volesse fondersi in un modo diverso. L'intrinseco dovrebbe in teoria darti il ​​meglio di entrambi i mondi per quanto riguarda il consentire all'ottimizzatore di fare un buon lavoro.)


https://gcc.gnu.org/wiki/DontUseInlineAsm se puoi evitarlo. Ma si spera che questa sezione sia utile se hai bisogno di capire il vecchio codice che usa inline asm in modo da poterlo riscrivere con intrinseci. Vedi anche https://stackoverflow.com/tags/inline-assembly/info