2015-05-03 13 views
7

Nel codice del kernel esiste una macro utilizzato per bit di prova (Linux versione 2.6.2): ​​differenza tra il rendimento funzione quando passa parametro tempo di compilazione costante o variabile

#define test_bit(nr, addr)      \ 
     (__builtin_constant_p((nr))    \ 
     ? constant_test_bit((nr), (addr))  \ 
     : variable_test_bit((nr), (addr))) 

cui sono definiti constant_test_bit e variable_test_bit come:

static inline int constant_test_bit(int nr, const volatile unsigned long *addr ) 
{  
     return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; 
} 


static __inline__ int variable_test_bit(int nr, const volatile unsigned long *addr) 
{  
     int oldbit; 

     __asm__ __volatile__(
       "btl %2,%1\n\tsbbl %0,%0" 
       :"=r" (oldbit) 
       :"m" (ADDR),"Ir" (nr)); 
     return oldbit; 
} 

capisco che __builtin_constant_p è utilizzato per rilevare se una variabile è tempo di compilazione costante o sconosciuta. La mia domanda è: c'è qualche differenza di prestazioni tra queste due funzioni quando l'argomento è una costante di tempo di compilazione o no? Perché usare la versione C quando è e utilizzare la versione di assemblaggio quando non lo è?

UPDATE: La seguente funzione principale è utilizzata per testare le prestazioni:

costante, chiamata constant_test_bit:

int main(void) { 
     unsigned long i, j = 21; 
     unsigned long cnt = 0; 
     srand(111) 
     //j = rand() % 31; 
     for (i = 1; i < (1 << 30); i++) { 
       j = (j + 1) % 28; 
       if (constant_test_bit(j, &i)) 
         cnt++; 
     } 
     if (__builtin_constant_p(j)) 
       printf("j is a compile time constant\n"); 
     return 0; 
} 

Questa emette correttamente la frase j è un ...

Per le altre situazioni basta decommentare la linea che assegna un numero "casuale" a j e modificare il nome della funzione in base LY. Quando questa riga non è commentata, l'output sarà vuoto e ciò è previsto.

uso gcc test.c -O1 compilare, e qui è il risultato:

costante, constant_test_bit:

$ time ./a.out 

j is compile time constant 

real 0m0.454s 
user 0m0.450s 
sys  0m0.000s 

costante, variable_test_bit (omettere time ./a.out, uguale per i seguenti):

j is compile time constant 

real 0m0.885s 
user 0m0.883s 
sys  0m0.000s 

variabile, constant_test_bit:

real 0m0.485s 
user 0m0.477s 
sys  0m0.007s 

variabile, variable_test_bit:

real 0m3.471s 
user 0m3.467s 
sys  0m0.000s 

ho ogni versione viene eseguito più volte, ed i risultati di cui sopra sono i valori tipici di loro. Sembra che la funzione constant_test_bit sia sempre più veloce della funzione variable_test_bit, indipendentemente dal fatto che il parametro sia una costante di tempo di compilazione o meno ... Per gli ultimi due risultati (quando j non è costante) la versione variabile è anche drammaticamente più lenta della costante uno. Mi manca qualcosa qui?

+0

Potrebbe essere, ma l'unico modo per scoprirlo è misurare. – deviantfan

+0

Ovviamente qualcuno ha pensato che potesse fare la differenza in perf, o non ci sarebbero state 2 versioni. Per i dettagli, hai 4 casi da considerare (passando una costante/non costante a una delle due funzioni). Cosa pensi che accada in ogni caso? Hai guardato l'assemblaggio generato? –

+0

@deviantfan Ho aggiunto i risultati delle prestazioni. –

risposta

5

Usando godbolt possiamo fare un experiment using of constant_test_bit, le seguenti due funzioni di prova sono compilati gcc con la bandiera -O3:

// Non constant expression test case 
int func1(unsigned long i, unsigned long j) 
{ 
    int x = constant_test_bit(j, &i) ; 
    return x ; 
} 

// constant expression test case 
int func2(unsigned long i) 
{ 
    int x = constant_test_bit(21, &i) ; 
    return x ; 
} 

Vediamo l'ottimizzatore è in grado di ottimizzare il caso espressione costante al seguente:

shrq $21, %rax 
andl $1, %eax 

mentre il caso espressione non costante finisce come segue:

sarl $5, %eax 
andl $31, %ecx 
cltq 
leaq -8(%rsp,%rax,8), %rax 
movq (%rax), %rax 
shrq %cl, %rax 
andl $1, %eax 

Quindi l'ottimizzatore è in grado di produrre un codice molto migliore per il caso di espressione costante e possiamo vedere che il caso non costante per constant_test_bit è piuttosto negativo rispetto al gruppo laminato a mano in variable_test_bit e l'implementatore deve credere all'espressione costante caso per constant_test_bit finisce per essere meglio di:

btl %edi,8(%rsp) 
sbbl %esi,%esi 

maggior parte dei casi.

Sul motivo per cui il vostro caso di test sembra mostrare una conclusione diversa è che il vostro caso di test è difettoso. Non sono stato in grado di scoprire tutti i problemi. Ma se guardiamo al this case utilizzando constant_test_bit con un'espressione non costante possiamo vedere l'ottimizzatore è in grado di spostare tutto il lavoro al di fuori l'aspetto ridurre il lavoro relativo al constant_test_bit all'interno del ciclo a:

movq (%rax), %rdi 

anche con una versione precedente di gcc, ma questo caso potrebbe non essere pertinente ai casi in cui viene utilizzato lo standard test_bit. Potrebbero esserci casi più specifici in cui questo tipo di ottimizzazione non sarà possibile.

+0

Ma perché utilizzare la versione variabile quando il parametro non è costante? L'assemblaggio della versione costante generata da gcc sembra ancora meglio della versione variabile in quel caso. –

+0

@XiangyuZhu ha aggiornato la mia risposta, il test ha alcuni problemi, è difficile metterli in discussione, ma è probabile che la ragione iniziale di questa scelta sia stata motivata dalle ragioni esposte nella mia risposta iniziale. Mi auguro che abbiano un benchmark con casi rilevanti e abbiano fatto in modo che l'assunto fosse davvero in forma ma difficile da sapere. –

+0

Sì, forse il modo in cui il mio codice è stato utilizzato per testare è così "speciale" che gcc è in grado di ottimizzarlo più di 'variable_test_bit'. Ho cercato il codice del kernel recente e ho scoperto che un codice simile è ancora lì, quindi ci deve essere una buona ragione per questo (usando diverse versioni della funzione). Comunque decido di lasciarlo solo per ora. Grazie per la tua risposta. –

Problemi correlati