2011-02-16 28 views
5

Ho il seguente programma C (una semplificazione del mio caso dell'uso effettivo che presenta lo stesso comportamento)Perché GCC non auto-vectorize questo loop?

#include <stdlib.h> 
#include <math.h> 
int main(int argc, char ** argv) { 
    const float * __restrict__ const input = malloc(20000*sizeof(float)); 
    float * __restrict__ const output = malloc(20000*sizeof(float)); 

    unsigned int pos=0; 
    while(1) { 
      unsigned int rest=100; 
      for(unsigned int i=pos;i<pos+rest; i++) { 
        output[i] = input[i] * 0.1; 
      } 

      pos+=rest;    
      if(pos>10000) { 
        break; 
      } 
    } 
} 

Quando compilo con

-O3 -g -Wall -ftree-vectorizer-verbose=5 -msse -msse2 -msse3 -march=native -mtune=native --std=c99 -fPIC -ffast-math 

ottenere l'output

main.c:10: note: not vectorized: unhandled data-ref 

dove 10 è la linea dell'anello interno per. Quando ho cercato il motivo per cui poteva dirlo, sembrava che stessero dicendo che i puntatori potevano essere alias, ma non possono essere nel mio codice, dato che ho la parola chiave __restrict. Hanno anche suggerito di includere i flag -msse, ma non sembrano fare nulla. Qualsiasi aiuto?

+0

Quale versione gcc? Potrebbe essere utile anche un esempio funzionante, dato che una versione compromessa è stata vettorizzata quando l'ho provata con 4.4.5 – ergosys

+0

potresti pubblicare il codice di esempio che compila? quando ho inserito alcuni valori fittizi, il ciclo è stato vettorizzato ... – Christoph

+0

@ergosys: cosa ha detto;) – Christoph

risposta

3

Sembra certamente un bug. Nelle seguenti, funzioni equivalenti, foo() è Vectorised ma bar() non è nella compilazione di un bersaglio x86-64:

void foo(const float * restrict input, float * restrict output) 
{ 
    unsigned int pos; 
    for (pos = 0; pos < 10100; pos++) 
     output[pos] = input[pos] * 0.1; 
} 

void bar(const float * restrict input, float * restrict output) 
{ 
    unsigned int pos; 
    unsigned int i; 
    for (pos = 0; pos <= 10000; pos += 100) 
     for (i = 0; i < 100; i++) 
      output[pos + i] = input[pos + i] * 0.1; 
} 

Aggiunta bandiera -m32, per compilare un x86 invece, provoca entrambe le funzioni da Vectorised .

+1

Grazie! Sono su una piattaforma a 64 bit! fare -m32 lo fa funzionare perfettamente. Sto registrando un bug report ora. Le altre risposte sono ottime, ma in realtà sono solo soluzioni alternative, in quanto ciò dovrebbe funzionare senza modifiche. –

+0

Si noti che l'eseguibile a 32 bit può essere molto più lento del 64 non vettorizzato, quindi, a meno che l'obiettivo non sia semplicemente "utilizzare SSE", è necessario profilare l'intera applicazione. –

+0

Grazie Ben, in realtà non sto usando questo per compilare il mio codice, ma solo per archiviare il bug report. Posso farlo per vettorizzare correttamente su 64 bit semplicemente riordinando le cose un po '. –

1

prova:

const float * __restrict__ input = ...; 
float * __restrict__ output = ...; 

esperimento un po 'cambiando le cose intorno:

#include <stdlib.h> 
#include <math.h> 

int main(int argc, char ** argv) { 

    const float * __restrict__ input = new float[20000]; 
    float * __restrict__ output = new float[20000]; 

    unsigned int pos=0; 
    while(1) { 
     unsigned int rest=100; 
     output += pos; 
     input += pos; 
     for(unsigned int i=0;i<rest; ++i) { 
      output[i] = input[i] * 0.1; 
     } 

     pos+=rest; 
     if(pos>10000) { 
      break; 
     } 
    } 
} 

g++ -O3 -g -Wall -ftree-vectorizer-verbose=7 -msse -msse2 -msse3 -c test.cpp 

test.cpp:14: note: versioning for alias required: can't determine dependence between *D.4096_24 and *D.4095_21 
test.cpp:14: note: mark for run-time aliasing test between *D.4096_24 and *D.4095_21 
test.cpp:14: note: Alignment of access forced using versioning. 
test.cpp:14: note: Vectorizing an unaligned access. 
test.cpp:14: note: vect_model_load_cost: unaligned supported by hardware. 
test.cpp:14: note: vect_model_load_cost: inside_cost = 2, outside_cost = 0 . 
test.cpp:14: note: vect_model_simple_cost: inside_cost = 2, outside_cost = 0 . 
test.cpp:14: note: vect_model_simple_cost: inside_cost = 2, outside_cost = 1 . 
test.cpp:14: note: vect_model_simple_cost: inside_cost = 1, outside_cost = 0 . 
test.cpp:14: note: vect_model_store_cost: inside_cost = 1, outside_cost = 0 . 
test.cpp:14: note: cost model: Adding cost of checks for loop versioning to treat misalignment. 

test.cpp:14: note: cost model: Adding cost of checks for loop versioning aliasing. 

test.cpp:14: note: Cost model analysis: 
    Vector inside of loop cost: 8 
    Vector outside of loop cost: 6 
    Scalar iteration cost: 5 
    Scalar outside cost: 1 
    prologue iterations: 0 
    epilogue iterations: 0 
    Calculated minimum iters for profitability: 2 

test.cpp:14: note: Profitability threshold = 3 

test.cpp:14: note: Vectorization may not be profitable. 
test.cpp:14: note: create runtime check for data references *D.4096_24 and *D.4095_21 
test.cpp:14: note: created 1 versioning for alias checks. 

test.cpp:14: note: LOOP VECTORIZED. 
test.cpp:4: note: vectorized 1 loops in function. 

Compilation finished at Wed Feb 16 19:17:59 
+0

Qual è la logica per questo? –

+0

@Oli solo un'ipotesi, potrebbe essere che il suo compilatore non gradisca il const extra o il modulo '__restrict' – Anycorn

+0

Non cambi nulla. –

2

E non gli piace il formato ciclo esterno che impedisce dalla comprensione del ciclo interno. Posso farlo per vettorizzare se ho appena piegarlo in un singolo ciclo:

#include <stdlib.h> 
#include <math.h> 
int main(int argc, char ** argv) { 
    const float * __restrict__ input = malloc(20000*sizeof(float)); 
    float * __restrict__ output = malloc(20000*sizeof(float)); 

    for(unsigned int i=0; i<=10100; i++) { 
      output[i] = input[i] * 0.1f; 
    } 
} 

(notare che io non ho pensato troppo a come tradurre correttamente il limite resto pos + in un singolo per la condizione del ciclo, potrebbe essere sbagliato)

Potrebbe essere in grado di sfruttare questo vantaggio inserendo un ciclo interno semplificato in una funzione chiamata con puntatori e conteggio. Anche quando è nuovamente inserito, potrebbe funzionare correttamente. Questo presuppone che tu abbia eliminato parti del tuo ciclo while() che ho appena semplificato, ma che devi conservare.

Problemi correlati