Supponiamo di provare a utilizzare il tsc per il monitoraggio delle prestazioni e vogliamo evitare il riordino delle istruzioni.Differenza tra rdtscp, rdtsc: memoria e cpuid/rdtsc?
Queste sono le nostre opzioni:
1:rdtscp
è una chiamata serializzazione. Impedisce il riordino della chiamata a rdtscp.
__asm__ __volatile__("rdtscp; " // serializing read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc variable
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
Tuttavia, rdtscp
è disponibile solo su CPU più recenti. Quindi in questo caso dobbiamo usare rdtsc
. Ma rdtsc
non è serializzato, quindi usarlo da solo non impedirà alla CPU di riordinarlo.
modo che possiamo utilizzare una di queste due opzioni per evitare riordino:
2: Questa è una chiamata alla cpuid
e poi rdtsc
. cpuid
è una chiamata di serializzazione.
volatile int dont_remove __attribute__((unused)); // volatile to stop optimizing
unsigned tmp;
__cpuid(0, tmp, tmp, tmp, tmp); // cpuid is a serialising call
dont_remove = tmp; // prevent optimizing out cpuid
__asm__ __volatile__("rdtsc; " // read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
3: Questa è una chiamata a rdtsc
con memory
nella lista clobber, che impedisce riordino
__asm__ __volatile__("rdtsc; " // read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc
:
: "%rcx", "%rdx", "memory"); // rcx and rdx are clobbered
// memory to prevent reordering
mia comprensione per il 3 ° opzione è la seguente:
Rendere il chiamare __volatile__
impedisce all'ottimizzatore di rimuovere asm o spostarlo attraverso qualsiasi istruzione che potrebbe richiedere i risultati (o modificare gli input) di asm. Tuttavia potrebbe ancora spostarlo rispetto alle operazioni non correlate. Quindi __volatile__
non è sufficiente.
Comunica che la memoria del compilatore è stata danneggiata: : "memory")
. Il clobber "memory"
significa che GCC non può fare supposizioni sul fatto che il contenuto della memoria rimanga lo stesso su ASM, e quindi non lo riordina attorno ad esso.
Quindi le mie domande sono:
- 1: E 'la mia comprensione della
__volatile__
e"memory"
corretta? - 2: le seconde due chiamate fanno la stessa cosa?
- 3: L'utilizzo di
"memory"
sembra molto più semplice dell'utilizzo di un'altra istruzione di serializzazione. Perché qualcuno dovrebbe usare la terza opzione sulla seconda opzione?
Sembra confondere il riordino delle istruzioni generate dal compilatore, che è possibile evitare usando 'volatile' e' memory' e il riordino delle istruzioni eseguite dal processore (noto anche come _out of order execution_), che si evita usando ' cpuid'. – hirschhornsalz
@hirschhornsalz ma non avrà 'memoria' nell'elenco dei clobber per impedire al processore di riordinare le istruzioni? La 'memoria' non si comporta come una barriera di memoria? –
o forse la 'memoria' nell'elenco dei clobber viene solo emessa in gcc e il codice macchina risultante non lo espone al processore? –