2015-10-17 28 views
6

Quando compilato con GCC 5.2 utilizzando -std=c99, -O3, e -mavx2, il seguente esempio di codice auto-vettorizza (assembly here):Come auto-vettorizzare le scritture stride con GCC?

#include <stdint.h> 

void test(uint32_t *restrict a, 
      uint32_t *restrict b) { 
    uint32_t *a_aligned = __builtin_assume_aligned(a, 32); 
    uint32_t *b_aligned = __builtin_assume_aligned(b, 32); 

    for (int i = 0; i < (1L << 10); i += 2) { 
    a_aligned[i] = 42 * b_aligned[i]; 
    a_aligned[i+1] = 3 * a_aligned[i+1]; 
    } 
} 

Ma il seguente codice di esempio non auto-vectorize (assembly here):

#include <stdint.h> 

void test(uint32_t *restrict a, 
      uint32_t *restrict b) { 
    uint32_t *a_aligned = __builtin_assume_aligned(a, 32); 
    uint32_t *b_aligned = __builtin_assume_aligned(b, 32); 

    for (int i = 0; i < (1L << 10); i += 2) { 
    a_aligned[i] = 42 * b_aligned[i]; 
    a_aligned[i+1] = a_aligned[i+1]; 
    } 
} 

L'unica differenza tra i campioni è il fattore di scala su a_aligned[i+1].

Questo è stato anche il caso per GCC 4.8, 4.9 e 5.1. L'aggiunta della dichiarazione volatile a a_aligned inibisce completamente l'auto-vettorizzazione. Il primo campione viene eseguito costantemente più velocemente del secondo per noi, con un aumento della velocità più pronunciato per i tipi più piccoli (ad esempio uint8_t anziché uint32_t).

C'è un modo per rendere auto-vectorize il secondo codice con GCC?

+0

Quindi l'unica differenza è il fattore di scala (3 rispetto a nulla)? Prova ad aggiungere 1 come fattore di scala in modo esplicito. Se lo risolve, è un bug del compilatore. – Jeff

+0

Oppure prova a commentare la frase 'a_aligned [i + 1] = a_aligned [i + 1]', o riscrivila come 'a_aligned [i + 1] * = 1'. Il compilatore potrebbe non sapere cosa fare con il tuo no-op self assignment se non quello di fare esattamente quello che hai detto di fare con esso. –

+0

@Jeff In effetti, l'unica differenza è il fattore di scala. L'aggiunta di un esplicito 1 non rende il secondo esempio di codice auto-vectorize ([assembly here] (https://goo.gl/dnjSaQ)). –

risposta

1

I seguenti vectorises versione, ma questo è brutto, se mi chiedete ...

#include <stdint.h> 

void test(uint32_t *a, uint32_t *aa, 
      uint32_t *restrict b) { 
    #pragma omp simd aligned(a,aa,b:32) 
    for (int i = 0; i < (1L << 10); i += 2) { 
    a[i] = 2 * b[i]; 
    a[i+1] = aa[i+1]; 
    } 
} 

di compilare con -fopenmp e per chiamare con test(a, a, b).

+0

Ci sono due caveat a questo approccio. Il primo è che perdi la parola chiave 'restrict' su' a' e 'aa', che potrebbero farti leggere' aa' più del necessario. Va bene per il codice di esempio fornito (in questo caso GCC dirama il codice non-vettorizzato se 'a' e 'aa' alias in modo fastidioso), ma in generale potrebbe comportare più letture del necessario. Ad esempio, considera se 'b' è stato sostituito con' a'; in questo caso, ci sono molte istruzioni 'vmovdq' aggiuntive che non sono necessarie. –

+0

Il secondo è che a '-O3', GCC si auto-in linea' test() '. Il che significa che nei punti in cui 'test()' è in linea, riconoscerà 'a' è' aa' e non riesce a auto-vectorize. Questo può essere risolto con '__attribute__ ((noinline))' di GCC, ma questo comporta ancora inutilmente sovraccarico di chiamata di funzione. –