2014-11-21 12 views
6

Ho cercato di utilizzare un semplice profiler per misurare l'efficienza di alcuni codici C su un server scolastico, e sto colpendo una situazione strana. Dopo un breve periodo di tempo (mezzo secondo secondo), il processore inizia improvvisamente ad eseguire le istruzioni due volte più velocemente. Ho provato per quasi tutti i possibili motivi a cui potrei pensare (memorizzazione nella cache, bilanciamento del carico sui core, frequenza della CPU modificata a causa dell'esaurimento del sonno), ma tutto sembra normale.Perché la mia CPU improvvisamente funziona il doppio più velocemente?

Per quello che vale, sto facendo questo test su un server linux della scuola, quindi è possibile che ci sia una configurazione insolita che non conosco, ma l'ID del processore in uso non cambia, e (via top) il server era completamente inattivo come ho provato.

codice

prova:

#include <time.h> 
#include <stdio.h> 

#define MY_CLOCK CLOCK_MONOTONIC_RAW 
// no difference if set to CLOCK_THREAD_CPUTIME_ID 

typedef struct { 
     unsigned int tsc; 
     unsigned int proc; 
} ans_t; 

static ans_t rdtscp(void){ 
     ans_t ans; 
     __asm__ __volatile__ ("rdtscp" : "=a"(ans.tsc), "=c"(ans.proc) : : "edx"); 
     return ans; 
} 

static void nop(void){ 
     __asm__ __volatile__ (""); 
} 

void test(){ 
     for(int i=0; i<100000000; i++) nop(); 
} 

int main(){ 
     int c=10; 
     while(c-->0){ 
       struct timespec tstart,tend; 
       ans_t start = rdtscp(); 
       clock_gettime(MY_CLOCK,&tstart); 
       test(); 
       ans_t end = rdtscp(); 
       clock_gettime(MY_CLOCK,&tend); 
       unsigned int tdiff = (tend.tv_sec-tstart.tv_sec)*1000000000+tend.tv_nsec-tstart.tv_nsec; 
       unsigned int cdiff = end.tsc-start.tsc; 
       printf("%u cycles and %u ns (%lf GHz) start proc %u end proc %u\n",cdiff,tdiff,(double)cdiff/tdiff,start.proc,end.proc); 
     } 
} 

uscita vedo:

351038093 cycles and 125680883 ns (2.793091 GHz) start proc 14 end proc 14 
350911246 cycles and 125639359 ns (2.793004 GHz) start proc 14 end proc 14 
350959546 cycles and 125656776 ns (2.793001 GHz) start proc 14 end proc 14 
351533280 cycles and 125862608 ns (2.792992 GHz) start proc 14 end proc 14 
350903833 cycles and 125636787 ns (2.793002 GHz) start proc 14 end proc 14 
350924336 cycles and 125644157 ns (2.793002 GHz) start proc 14 end proc 14 
349827908 cycles and 125251782 ns (2.792997 GHz) start proc 14 end proc 14 
175289886 cycles and 62760404 ns (2.793001 GHz) start proc 14 end proc 14 
175283424 cycles and 62758093 ns (2.793001 GHz) start proc 14 end proc 14 
175267026 cycles and 62752232 ns (2.793001 GHz) start proc 14 end proc 14 

ottengo un risultato simile (con esso prendendo un diverso numero di test per raddoppiare in termini di efficienza) con diversi livelli di ottimizzazione (-O0 a -O3).

Potrebbe forse avere qualcosa a che fare con l'hyperthreading, dove due core logici in un nucleo fisico (il server sta usando Xeon X5560s che può avere questo effetto) possono in qualche modo "unire" per formare un processore due volte più veloce?

+0

Non è solo un evento comune della limitazione della CPU? – xbug

+0

All'inizio pensavo che fosse la limitazione della CPU, ma sto calcolando il GHz da cicli/tempo ed è molto coerente a 2.793 (che è quello che/proc/cpuinfo riporta come velocità). Se la CPU fosse sottoposta a limitazioni, il valore non sarebbe dimezzato? – dooglius

+2

Si dovrebbe leggere su 'Turbo Boost', che sono abbastanza sicuro è disponibile su quella CPU. – Petesh

risposta

0

Alcune CPU hanno ottimizzazioni sul chip, che stanno imparando il percorso che il vostro codice richiede normalmente. Prevedendo con successo quale sarebbe stata la prossima istruzione if, non è necessario scartare la coda e caricare tutte le nuove operazioni da zero. A seconda del chip e dell'algoritmo, potrebbero essere necessari da 5 a 10 cicli, fino a quando non viene eseguita correttamente la previsione delle istruzioni if. Ma in qualche modo ci sono anche ragioni che parlano contro questo come la ragione di questo comportamento.

Guardando il tuo output, direi che questo potrebbe anche essere solo lo sheduling del sistema operativo e/o il regolatore di frequenza CPU utilizzato lì. Sei sicuro che la frequenza della CPU non cambi durante l'esecuzione del tuo codice? Nessun aumento della CPU? Gli strumenti di linux come cpufreq sono spesso usati per regolare la frequenza della CPU.

+0

Il mio voto è [previsione del ramo] (http://en.wikipedia.org/wiki/Branch_predictor) e anche – Soren

+7

I tempi qui coinvolti sono troppo grandi perché questo possa essere un artefatto di previsione del ramo. Un predittore di branche che richiede milioni di cicli per "bloccarsi" è altrettanto inutile. – Mysticial

+0

Hai completamente ragione. Dovrebbe essersi bloccato molto prima. Solo il ciclo più esterno potrebbe richiedere così tanto tempo da prevedere, ma questo non ha quasi alcun contributo al tempo di esecuzione. – user3387542

-1

Hyper-threading significa replicare lo spazio del registro, non le effettive unità di decodifica/esecuzione, quindi questa non è una soluzione.

Per testare l'accuratezza del metodo di micro-benchmark vorrei fare le seguenti operazioni:

  1. Eseguire il programma con priorità alta
  2. Contare il numero di istruzioni per vedere se è corretto. Lo farei usando perf stat ./binary - questo significa che devi avere perf. Lo farei più volte e osserverò le metriche di orologi e istruzioni per vedere come possono essere eseguite più istruzioni in un singolo ciclo.

mi hanno alcune osservazioni aggiuntive :

Per ogni NOP anche ad un confronto e di un salto condizionato nel ciclo for.Se davvero si vuole eseguire PON che avrei scritto una dichiarazione come questa:

#define NOP5 __asm__ __volatile__ ("nop nop nop nop nop"); 
#define NOP25 NOP5 NOP5 NOP5 NOP5 NOP5 
#define NOP100 NOP25 NOP25 NOP25 NOP25 
#define NOP500 NOP100 NOP100 NOP100 NOP100 NOP100 
... 
for(int i=0; i<100000000; i++) 
{ 
    NOP500 NOP500 NOP500 NOP500 
} 

Questo costrutto vi permetterà di effettivamente fare NOP di invece di confrontare i con 100M.

+0

Non ho ottenuto ciò che si pensa riduca la larghezza di banda di esecuzione/decodifica, ma +1 per suggerire di cambiare il rapporto nop/ramo. – Leeor

+0

Downvoted per suggerire che la predizione di ramo o un processore superscaler che sfrutta l'ILP provocherebbe questo. Questo è sbagliato al 100%. –

+0

@CraigAnderson perché è sbagliato? Dal momento che il ridimensionamento della frequenza è altamente improbabile quale è la ragione? – VAndrei

1

Alcuni sistemi aumentano la velocità del processore in base al carico del sistema. Come si nota giustamente, questo è particolarmente fastidioso durante l'analisi comparativa.

Se il server è in esecuzione Linux, si prega di digitare

cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor 

Se questa uscita ondemand, powersave o userspace, quindi CPU frequency scaling è attivo, e si sta andando a trovare molto difficile da fare benchmark. Se questo dice performance, il ridimensionamento della frequenza della CPU è disabilitato.

+0

Nelle prime esecuzioni esce 2,7 ghz. Per ottenere le prestazioni delle ultime corse è necessario 5.4 ghz ... Sei proprio sicuro che sia a regime? Inoltre, si prega di commentare quando downvoting. – VAndrei

+0

@ VAndrei Ho fatto una deviazione dal momento che le risposte erano chiaramente errate: non è possibile che la previsione delle filiali o l'esecuzione superscalare avvengano a quei tempi. C'era già un commento in tal senso, quindi mi sono astenuto dall'aggiungere un commento duplicato. – jch

+0

jch, quali sono i tuoi argomenti contro le nostre risposte? user3387542 ha già dichiarato la possibilità di ridimensionamento della frequenza e ha anche indicato i governatori, ma ha ridimensionato la sua risposta. Ho anche aggiunto ulteriori informazioni e un metodo di convalida. Ho anche sfidato la tua risposta, ma non hai fornito una discussione. – VAndrei

Problemi correlati