2011-02-16 7 views
5

Tengo el siguiente programa en C (una simplificación de mi caso de uso real que presenta el mismo comportamiento)¿Por qué GCC no auto-vectorizará este ciclo?

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

Cuando compilo con

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

que obtener la salida

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

donde 10 es la línea del ciclo for interno. Cuando miré por qué podría decir esto, parecía estar diciendo que los punteros podrían tener un alias, pero no pueden estar en mi código, ya que tengo la palabra clave __restrict. También sugirieron incluir las banderas -msse, pero tampoco parecen hacer nada. ¿Alguna ayuda?

+0

¿Qué versión gcc? Un ejemplo de trabajo podría ser útil también, como una versión pirateada vectorizada cuando lo intenté con 4.4.5 – ergosys

+0

¿podría publicar código de ejemplo que se compile? cuando completé algunos valores ficticios, el lazo fue vectorizado ... – Christoph

+0

@ergosys: lo que dijo;) – Christoph

Respuesta

3

Parece un error. En los siguientes, funciones equivalentes, foo() se vectorizado pero bar() no es, cuando se compila para un objetivo 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; 
} 

Adición de la bandera -m32, compilar para un objetivo x86 lugar, hace que ambas funciones a ser vectorizados .

+1

¡Gracias! ¡Estoy en una plataforma de 64 bits! doing -m32 hace que funcione perfectamente. Estoy presentando un informe de error ahora. Las otras respuestas son geniales, pero en realidad son solo soluciones, ya que esto debería funcionar sin modificaciones. –

+0

Tenga en cuenta que el ejecutable de 32 bits puede ser mucho más lento que el 64 no vectorizado, por lo tanto, a menos que su objetivo sea simplemente "usar SSE", debe perfilar toda la aplicación. –

+0

Gracias Ben, en realidad no estoy usando esto para compilar mi código, sino solo para presentar el informe de error. Puedo hacer que vectorice correctamente en 64 bits simplemente reorganizando las cosas un poco. –

1

intento:

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

experimento un poco por cambiar las cosas:

#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

¿Cuál es la razón de esto? –

+0

@Oli solo una conjetura, puede ser que a su compilador no le gusta el formulario const o '__restrict' – Anycorn

+0

No cambia nada. –

2

No le gusta el formato de bucle exterior que se le impide comprender el bucle interno. Puedo conseguirlo para vectorizar si sólo doblarlo en un solo bucle:

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

(tenga en cuenta que no pensé demasiado en cómo traducir correctamente el límite resto pos + en una sola condición en bucle, puede estar equivocado)

Es posible que pueda aprovechar esto al poner un bucle interno simplificado en una función a la que llama con punteros y un conteo. Incluso cuando está alineado de nuevo, puede funcionar bien. Esto supone que borraste partes de tu loop while() que acabo de simplificar pero que debes retener.

Cuestiones relacionadas