Estoy tratando de vectorizar algunos cálculos simples para acelerar la arquitectura SIMD. Sin embargo, también quiero ponerlos como función en línea porque las llamadas a funciones y los códigos no vectorizados también requieren tiempo de cálculo. Sin embargo, no siempre puedo lograrlos al mismo tiempo. De hecho, la mayoría de mis funciones en línea no se auto-vectorizan. Aquí hay un código de prueba simple que funciona:

inline void add1(double *v, int Length) {
    for(int i=0; i < Length; i++) v[i] += 1;
}

void call_add1(double v[], int L) {
    add1(v, L);
}

int main(){return 0;}

En Mac OS X 10.12.3, compílelo:

clang++ -O3 -Rpass=loop-vectorize -Rpass-analysis=loop-vectorize -std=c++11 -ffast-math test.cpp

test.cpp:2:5: remark: vectorized loop (vectorization width: 2, interleaved count: 2) [-Rpass=loop-vectorize]
    for(int i=0; i < Length; i++) v[i] += 1;
    ^

Sin embargo, algo muy similar (solo mover argumentos en call_add1) no funciona:

inline void add1(double *v, int Length) {
    for(int i=0; i < Length; i++) v[i] += 1;
}

void call_add1() {
    double v[20]={0,1,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9}; 
    int L=20;
    add1(v, L);
}

int main(){ return 0;}

Compilar con el mismo comando no produce resultados. ¿Por qué pasó esto? ¿Cómo puedo asegurarme de que los bucles en las funciones en línea siempre se auto-vectorizan? Quiero vectorizar muchos bucles de funciones, así que espero que la solución no sea demasiado compleja.

4
Jiang-Nan Yang 15 ene. 2018 a las 19:55

3 respuestas

La mejor respuesta

Compilar su código con -fsave-optimization-record muestra que el ciclo se desenrolló y luego se eliminó.

--- !Passed
Pass:            loop-unroll
Name:            FullyUnrolled
DebugLoc:        { File: main.cpp, Line: 2, Column: 5 }
Function:        _Z9call_add1v
Args:            
  - String:          'completely unrolled loop with '
  - UnrollCount:     '20'
  - String:          ' iterations'
...
--- !Passed
Pass:            gvn
Name:            LoadElim
DebugLoc:        { File: main.cpp, Line: 2, Column: 40 }
Function:        _Z9call_add1v
Args:            
  - String:          'load of type '
  - Type:            double
  - String:          ' eliminated'
  - String:          ' in favor of '
  - InfavorOfValue:  '0.000000e+00'

Si coloca 4000 elementos en la matriz, excederá el umbral del optimizador y el sonido metálico permitirá la vectorización.

8
ivaigult 15 ene. 2018 a las 17:18

Parece que el compilador simplemente desenrollaría y optimizaría el ciclo, cuando v se especifica explícitamente. Lo cual es una cosa buena : el código que no tiene que ejecutarse es el más rápido.

Para verificar que es una optimización, puede intentar hacer que algunas de las variables sean volátiles (ejemplo en vivo).

3
AMA 15 ene. 2018 a las 17:20

Eso es porque para el compilador del segundo caso sabe que no hay efectos secundarios y optimiza todo https://godbolt.org/ g / CnojEi clang 4.0.0 con -O3 solo hojas:

call_add1():
  rep ret
main:
  xor eax, eax
  ret

Y no obtienes marketing sobre la magia del bucle.

En el primer caso, el compilador produce algún cuerpo para la función, porque la función modifica el argumento. Si compiló esto como un archivo objeto. Podría vincular a esta función y funcionaría. Supongo que si los parámetros fueran constantes, entonces tal vez la función también quedaría con el cuerpo vacío.

Cuando imprime el contenido, los programas no son idénticos, pero ambos usan instrucciones vectorizadas: https://godbolt.org/ g / KF1kNt

4
luk32 15 ene. 2018 a las 17:14