2011-02-16 5 views
5

mam następujący program C (uproszczenia moim konkretnym przypadku zastosowania, który wykazuje takie samo zachowanie)Dlaczego GCC nie będzie automatycznie wektoryzować tej pętli?

#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; 
      } 
    } 
} 

Kiedy skompilować z

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

I wyprowadzał

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

gdzie 10 jest linią wewnętrznej pętli for. Kiedy spojrzałem na to, dlaczego tak się mówi, wydawało się, że można powiedzieć, że wskaźniki mogą być wygładzane, ale nie mogą być w moim kodzie, ponieważ mam słowo kluczowe __restrict. Zasugerowali także włączenie flag -msse, ale i oni nie robią nic. Jakaś pomoc?

+0

Jaką wersję gcc? Działający przykład może być również użyteczny, ponieważ wersja zhakowana była wektoryzowana, gdy próbowałem go z 4.4.5 – ergosys

+0

czy mógłbyś wstawić przykładowy kod, który kompilował? kiedy wypełniłem jakieś fałszywe wartości, pętla była wektoryzowana ... – Christoph

+0

@ergosys: co on powiedział;) – Christoph

Odpowiedz

3

To z pewnością wygląda na błąd. W dalszej części równoważnych funkcjach, foo()bar() jest wektoryzowane ale nie jest przy kompilacji za cel 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; 
} 

dodanie flagi -m32, aby skompilować dla x86 zamiast powoduje obie funkcje mają być wektoryzowane .

+1

Dziękujemy! Jestem na platformie 64-bitowej! robienie -m32 sprawia, że ​​działa idealnie. Zgłaszam teraz raport o błędzie. Inne odpowiedzi są świetne, ale tak naprawdę są tylko obejściami, ponieważ to powinno działać bez modyfikacji. –

+0

Zauważ, że 32-bitowy plik wykonywalny może być znacznie wolniejszy niż nie-wektorowy 64, więc jeśli Twoim celem nie jest wyłącznie "użyj SSE", powinieneś profilować całą aplikację. –

+0

Dzięki Ben, w rzeczywistości nie używam tego do kompilacji mojego kodu, ale po to, aby złożyć raport o błędzie. Mogę go poprawnie zwerbalizować na 64-bitowym, zmieniając trochę układ. –

1

try:

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

Eksperyment nieco zmieniając rzeczy wokół:

#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

Jakie są tego powody? –

+0

@Oli tylko zgadnij, może być jego kompilator nie lubi dodatkowej stałej lub '__restrict' postaci – Anycorn

+0

Nic nie zmienia. –

2

To nie podoba się zewnętrzną formatu pętli który zapobiegając jej zrozumieniu wewnętrznej pętli. mogę zmusić go do wektoryzacji jeśli po prostu złóż go w jednej pętli:

#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; 
    } 
} 

(zauważ, że nie myśleć zbyt intensywnie o jak poprawnie przetłumaczyć Pos + limitu reszta w jeden dla warunku pętli to może być źle)

Być może będziesz w stanie z tego skorzystać, umieszczając uproszczoną wewnętrzną pętlę w funkcji wywoływanej przez wskaźniki i licznik. Nawet jeśli jest ponownie zainicjowany, może działać poprawnie. Zakłada to, że usunąłeś część swojej pętli while(), którą właśnie uprościłem, ale musisz ją zachować.