2016-02-29 9 views
5

Ecco del codice abbastanza semplice, compilato con -O2 (gcc 4.8.5):ciò ferma qualificazione GCC __restrict__ da lavorare

unsigned char * linebuf; 
int yuyv_tojpegycbcr(unsigned char * buf, int w) 
{ 
    int col; 
    unsigned char * restrict pix = buf; 
    unsigned char * restrict line = linebuf; 

    for(col = 0; col < w - 1; col +=2) 
    { 
      line[col*3] = pix[0]; 
      line[col*3 + 1] = pix[1]; 
      line[col*3 + 2] = pix[3]; 
      line[col*3 + 3] = pix[2]; 
      line[col*3 + 4] = pix[1]; 
      line[col*3 + 5] = pix[3]; 
      pix += 4; 
    } 
    return 0; 
} 

e qui è il gruppo corrispondente:

0000000000000000 <yuyv_tojpegycbcr>: 
    0: 83 fe 01    cmp $0x1,%esi 
    3: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax  # a <yuyv_tojpegycbcr+0xa> 
    a: 7e 4e     jle 5a <yuyv_tojpegycbcr+0x5a> 
    c: 83 ee 02    sub $0x2,%esi 
    f: 31 d2     xor %edx,%edx 
    11: d1 ee     shr %esi 
    13: 48 8d 74 76 03   lea 0x3(%rsi,%rsi,2),%rsi 
    18: 48 01 f6    add %rsi,%rsi 
    1b: 0f 1f 44 00 00   nopl 0x0(%rax,%rax,1) 
    20: 0f b6 0f    movzbl (%rdi),%ecx 
    23: 48 83 c2 06    add $0x6,%rdx 
    27: 48 83 c7 04    add $0x4,%rdi 
    2b: 48 83 c0 06    add $0x6,%rax 
    2f: 88 48 fa    mov %cl,-0x6(%rax) 
    32: 0f b6 4f fd    movzbl -0x3(%rdi),%ecx 
    36: 88 48 fb    mov %cl,-0x5(%rax) 
    39: 0f b6 4f ff    movzbl -0x1(%rdi),%ecx 
    3d: 88 48 fc    mov %cl,-0x4(%rax) 
    40: 0f b6 4f fe    movzbl -0x2(%rdi),%ecx 
    44: 88 48 fd    mov %cl,-0x3(%rax) 
    47: 0f b6 4f fd    movzbl -0x3(%rdi),%ecx 
    4b: 88 48 fe    mov %cl,-0x2(%rax) 
    4e: 0f b6 4f ff    movzbl -0x1(%rdi),%ecx 
    52: 88 48 ff    mov %cl,-0x1(%rax) 
    55: 48 39 f2    cmp %rsi,%rdx 
    58: 75 c6     jne 20 <yuyv_tojpegycbcr+0x20> 
    5a: 31 c0     xor %eax,%eax 
    5c: c3      retq 

Quando compilato senza il qualificatore restrict, l'output è identico: Un sacco di carichi e store intermixati. Alcuni valori vengono caricati due volte e sembra che non sia avvenuta alcuna ottimizzazione. Se pix e line non sono corretti, mi aspetto che il compilatore sia abbastanza intelligente e, tra le altre cose, carica pix [1] e pix [3] solo una volta.

Sei a conoscenza di qualcosa che potrebbe squalificare il qualificatore restrict?

PS: Con un nuovo gcc (4.9.2), su un'altra architettura (arm v7), il risultato è simile. Ecco uno script di test per confrontare il codice generato con e senza restrizioni.

#!/bin/sh 
gcc -c -o test.o -std=c99 -O2 yuyv_to_jpegycbcr.c 
objdump -d test.o > test.S 


gcc -c -o test2.o -O2 -D restrict='' yuyv_to_jpegycbcr.c 
objdump -d test2.o > test2.S 
+0

Qualsiasi motivo non si utilizza il qualificatore standard 'restrict'? – Olaf

+0

perché l'utilizzo di std = c99 interrompe il mio codice, probabilmente perché non ho impostato le funzionalità valide_test_macro. Posso risolvere il problema ma non penso che farebbe la differenza. – shodanex

+0

Anche qui ci si deve aspettare la vettorizzazione (supponendo che il dispositivo che si sta compilando lo supporti). – marko

risposta

4

Inserire le restrizioni sui parametri di funzione anziché sulle variabili locali.

Dalla mia esperienza, la maggior parte dei compilatori (incluso GCC) utilizza il limite solo se è specificato nei parametri della funzione. Tutti gli usi sulle variabili locali all'interno di una funzione vengono ignorati.

Sospetto che questo abbia a che fare con l'analisi di aliasing eseguita a livello di funzione piuttosto che a livello di blocco di base. Ma non ho prove per confermare questo. Inoltre, probabilmente varia in base alla versione del compilatore e del compilatore.

In entrambi i casi, questo tipo di cose sono piuttosto schizzinose su cui fare affidamento. Quindi, se la prestazione è importante, la si ottimizza manualmente o si ricorda di rivisitarla ogni volta che si aggiorna o si modificano i compilatori.

+1

Per commento su https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60712, sembra che gcc applichi solo restrizioni al parametro funzione – shodanex

+0

Sto vedendo esattamente questo con la moltiplicazione della matrice di blocchi. Questo ha sei loop. Se inserisco i tre anelli più interni in una funzione statica con parametri limitati, il codice è due volte più veloce di quando non dichiaro una funzione. Vedo lo stesso effetto con GCC (6.3) e Clang (4.0). Quindi sembra che i compilatori ignorino le variabili locali con restrizioni esattamente come dici tu. Non so di ICC. –

+0

E anche a volte quando dichiaro una funzione statica separata il compilatore dà un risultato peggiore. A volte devo dichiarare la funzione interna con restrizioni in un file oggetto separato. Quindi devi fare esattamente quello che dici: controlla il montaggio la prima volta e ogni aggiornamento o modifica del compilatore. –