Vectorización

Índice
1.
2.
3.
4.
5
5.
6.
7.
8.
9.
Arquitectura y Computación
de Alto Rendimiento
Vectorización
Introducción
Compilación
Bucles vectorizables y dificultades
Mensajes del Compilador
P di consejo
Pedir
j all compilador
il d
Directivas OpenMP
Directivas Intel
Intrinsics y clases de C++. Prefetch
Ejemplo
2
Introducción


Introducción
Los procesadores tienen unidades vectoriales
que se usan con instrucciones SIMD
Hereda la idea de antiguos superordenadores
(Cray) e Intel lleva tiempo con ello:
MMX (Matrix
(M t i Math
M th Extensions):
E t
i
) 64 bits
bit
SSE 1-5 (Streaming SIMD Extensions): 128 bits
● AVX 1-2 (Advanced Vector Extensions): 256 bits
● IMCI (Initial Many Core Instructions): 512 bits
●
●

Bucle escalar
for (i=0; i<n ; i++)
C[i] = A[i] + B[i];
Intel Xeon Phi ejecuta 16 instrucciones SP o 8
de DP por ciclo (Con FMA 32 SP o 16 DP)
3
Introducción
4
Esquema general
Bucle SIMD o vectorizado (pseudo código)
for ( i=0; i<n ; i+=4 )
C[i:(i+4)] = A[i:(i+4)]+B[i:(i+4)];
5
AC : MP: Introd. a OpenMP
6
1
Introducción
Varias opciones
Múltiples operandos posibles
7
AC : MP: Introd. a OpenMP
Compilación

Bucles vectorizables
icc

-O3 vectoriza
● -no-vec: No vectoriza (tb –no-simd )
● -vec-report=n: Informa de la vectorización (0..7)
● -xhost
xhost o -mhost:
mhost: indica la arquitectura


●

gcc


-O3 vectoriza
-ftree-vectorize: vectoriza
● -ftree-vectorizer-verbose=n: Informa de la
vectorización (0..9)
● -mhost: indica la arquitectura
●
●
Contables, a la entrada debe saberse el número
de iteraciones, que no puede cambiar
Bucles sencillos, con una entrada y salida
Evitar saltos (if, goto, switch), el comportamiento
de todas las iteraciones es el mismo
mismo, aunque if
sencillos se permiten. (usar peeling)
Bucle más interno (hay collapse, interchange,..)
Sin llamadas a funciones, a menos que tengan
versión vectorial (sin,cos,log,sqrt,max,pow,exp…)
Podemos hacerla con las funciones elementales
O hacerla inline
●
●
9
10
Dificultades en la vectorización

Datos no contiguos, es vectorizable pero lento:
●
●
●




Ejemplos de mensajes del compilador

icc
Low trip count
Not Inner Loop
● Existence of vector dependence (aliasing)
●
●
•
No suele ser vectorizable menos tipo reduction
•
Ambigüedades en el uso de punteros
●

Contador incrementa de N en N,
Uso de indirecciones
Vector de estructuras (mejor estructura de vect)
Dependencias entre iteraciones
●
8
Mejor notación vectorial que punteros
Datos no alineados o condiciones frontera
(provoca peeling).
Uso del long double (usar menor tamaño posible)
11
Uso de random o modulo
usar #pragma ivdep (Intel)
O usar restrict (std=c99):
void copy(char * restrict a, char * restrict b, int n)
Vectorization possible but seems inefficient
Condition may protect exception (ivdep)
● data type unsupported on given target architecture
●
●
12
2
Ejemplos de mensajes del compilador

Obteniendo información
icc
●
●
●
●
●
Statement cannot be vectorized (switch, loop)
Subscript too complex
Unsupported Loop Structure
Top test could not be found
Operator unsuited for vectorization

Al compilar con opción -vec-report2 informa de
la vectorización de bucles en Host y MIC

icc -vec-report2 -openmp offload.c
offload.c(57): (col. 2) remark: *MIC* LOOP WAS
VECTORIZED.
offload c(54): (col
offload.c(54):
(col. 7) remark: *MIC* loop was
not vectorized: not inner loop.
offload.c(53): (col. 5) remark: *MIC* loop was
not vectorized: not inner loop.
 Opciones de 0 a 6, también 7 pero la salida es
para herramientas (Script Python VecAnalysis)
13
ACAR: Introd. al Intel Xeon Phi
Obteniendo información

Pedir consejo al compilador
Al compilar con -vec-report nos informa de:

-guide: Genera un informe más detallado que
los logs, dando consejos y chequeos a seguir

lucas.c(372): remark #30513: (VECT) Insert a
"#pragma ivdep" statement right before the loop
at line 372 to vectorize the loop. [VERIFY] Make
sure that these arrays in the loop do not have
unsafe cross-iteration dependencies: It, Ix, Iy.
A cross-iteration dependency exists if a memory
location is modified in an iteration of the loop
and accessed (by a read or a write) in another
iteration of the loop. Make sure that there are
no such dependencies, or that any cross-iteration
dependencies can be safely ignored.
Number of advice-messages emitted for this
compilation session: 1.
● n=0: Nada de información
● n=1: (por defecto) Bucles vectorizados
● n=2: tb bucles no vectorizados y por qué
● n=3: Añade información de dependencias
● n=4: Informa sólo de bucles no vectorizados
● n=5: Como el n=4 pero añade dependencias
● n=6: Información muy detallada
● n=7: Como el 6 pero para herramientas
(VecAnalysis)
ACAR: Introd. al Intel Xeon Phi
15
16
Directiva OpenMP para vectorizar






14
Directiva para funciones vectoriales
#pragma omp simd: Que vectorice si puede
Admite clausulas: reduction, collapse,private,…
Admite bucles “externos”
Se le puede añadir safelen(n) indicando
número iteraciones seguras, sin dependencias
También linear(list[:step]) que indica variables
que se incrementan con “step” a cada iteración
aligned(list[:alignment])






17
#pragma omp declare simd: Que genere
versión escalar y vectorial de la función
Admite clausulas: reduction, linear, aligned,…
simdlen(n) longitud del vector que debe
soportar la
l ffunción
ió vectorial
i l
uniform(list) indica variables constantes
Inbranch: siempre se llama desde un if
Notinbranch: nunca se llama desde un if
18
3
Vectorización con OpenMP


Directivas Intel para ayudar al compilador
La directiva simd se puede combinar con for
(reparte iteraciones vectorizadas entre hilos)
También con parallel y for

icc
●
#pragma ivdep: Que ignore las dependencias
•
●
●
●
●
●
Se le asegura que no las hay (punteros)
#pragma loop count (n): Nº iteraciones típico
#pragma vector always: Que vectorice si puede
#pragma vector aligned: Asegura datos alineados
#pragma novector: Que no vectorice
#pragma vector nontemporal: Todos los datos
serán sobreescritos, no hace falta leerlos
20
Directivas intel para ayudar al compilador





Funciones vectoriales o elementales (Intel)
#pragma simd: Que vectorice si puede
Es “similar” al OpenMP, admite clausulas
adicionales (reduction, private,…)
Admite bucles “externos”
Se le puede añadir vectorlength(n) indicando
tamaño del vector que es seguro, sin
dependencias
También “linear(…)” que indica variables que
se pueden deducir del índice del bucle





Se pueden generar versiones vectoriales de
ciertas funciones
Facilita la vectorización de bucles que las usen
No aplicable si es inline
También se genera la versión escalar
__declspec(vector) o
__attribute__((vector))
21
22
Compiler Intrinsics
Versiones de intrinsics con mask
// IMCI Intrisics
// SSE2 Intrinsics
for (int i=0; i<n; i+=16) {
for (int i=0; i<n; i+=4) {
__m512 Avec=_mm512_load_ps(A+i);
__m128 Avec=_mm_load_ps(A+i);
__m512 Bvec=_mm512_load_ps(B+i);
__m128 Bvec=_mm_load_ps(B+i);
Avec=_mm512_add_ps(Avec, Bvec);
Avec=_mm_add_ps(Avec, Bvec);
_mm512_store_ps(A+i, Avec);
_mm_store_ps(A+i, Avec);
}
}




P
Pseudo-ensamblador:
d
bl d V
Vectorizar
t i
a mano.
Vectores alineados: float A[n] y B[n] a 16 bytes
(SSE2) o a 64 bytes (IMCI) (#include <immintrin.h>)
“n” múltiplo de 4 (SSE) o de 16 (IMCI)
Carga en registros vectoriales, suma y guarda
// Versión escalar
for (int i=0; i<n; i+=1)
A[i] = A[i] + C[i];
AC : MP: Introd. a OpenMP
23

Hay una máscara para determinar sobre que
elemento se hace o no la operación vectorial
// Version escalar
for i = 1 to 16
if a[i] < b[i]
z[i] = x[i] * y[i];
// Version intrinsics
#include <immintrin.h>
__m512i z, x, y;
__mmask k = _mm512_cmplt_ps(a, b);
// Asigna z o x*y según k
z = _mm512_mask_mul_ps(z, k, x, y);
AC : MP: Introd. a OpenMP
24
4
Clases de C++



Interfaz de intrinsics. Más legible
Se protege con #ifdef __MIC__
Varias clases: F32vec16, F64vec8, I32vec16, Is32vec16,


Y SSE (F32vec4, F64vec2) o AVX(F32vec8, F64vec4)
Se puede combinar con genéricos:
Ejemplo: Intrinsics vs clases
//
Version Escalar
float a[16], r[16];
for i = 0 to 15
r[i] = exp(a[i])
//
Intrinsics
#include <immintrin.h>
__m512 vr, va = &a[0];
vr = __mm512_exp_ps(va);
*(__mm512 *)&(r[0]) = vr;
//
Vector Classes
#include <micvec.h>
F32vec16 vr, va = &a[0];
vr = exp(va); // Bastante más legible
*(F32vec16 *)&(r[0]) = vr;
Iu32vec16, I64vec8
SIMDType foo<SIMDType, BasicType>(SIMDType a)
F32vec8 res = foo<F32vec8, float>(a)

AC : MP: Introd. a OpenMP
25
AC : MP: Introd. a OpenMP
Prefetch


Para reducir fallos de cache
Manualmente con intrisics:

O con pragma (tb noprefetch)
Ejemplo
Calcular el potencial eléctrico en varios puntos
del espacio cuando hay m cargas, cada una
con carga electrica qi en la posición indicada

por el vector ri
 Para
punto, situado donde indica el vector
 cada punto
R, el potencial eléctrico viene dado por:
con:

_mm_prefetch((char *) &a[i], hint);
#pragma prefetch a [:hint[:distance]]



26
Distance en número iteraciones en avance
Hint distintos modos (L1-L2, exclusivo, temporal)
El Hw hace prefetching, el compilador también,
puede interferir. Desactivarlos para más control
AC : MP: Introd. a OpenMP
27
28
29
class Charge f { // Inefficient
public:
float x,y,z,q; // Coord. & charge
charge();
~charge();
};
void calculate_electric_potential(
const int m, // N. of charges
const int n, // N. of points in each dim.
const Charge* chg, // Array of Charges
float* const phi, // Out: elec. Potential
const float ds // Spatial grid spacing
)
30
Ejemplo
Ejemplo
AC : MP: Introd. a OpenMP
5
Ejemplo
Ejemplo: Mejoras
// Carga desde n*n puntos u “observadores”
for (int c=0; c<n*n ; c++) {
const float Rx=ds*(float)(c/n);//X-coord
const float Ry=ds*(float)(c%n);//Y-coord
const float Rz=ds*(float)(0);//Z-coord (0)
f
for
(i
(int
t i=0;
i 0 i<
i<m; i++){// autovectorized
t
t i d

Tres cambios:
●
AoS to SoA: Accesos contiguos
•
●
// Non-unit stride: (&chg[i+1].x - &chg[i].x) != sizeof(float)
const float dx=chg[i].x - Rx;
const float dy=chg[i].y - Ry;
const float dz=chg[i].z - Rz;
phi[c] -=chg[i].q/sqrtf(dx*dx+dy*dy+dz*dz);
●
Evitar dependencias (restrict o pragma ivdep)
Datos alineados a 32 bytes:
•
•
•
●
•
}
31
class Charge f { Ejemplo
public:
const int m;
float *x,*y,*z,*q; // Coord. & charge
charge(const int M): m(M) {
x=(float*)_mm_malloc(m*sizeof(float),32);
y=(float*)_mm_malloc(m*sizeof(float),32);
(fl t*)
ll ( * i
f(fl t) 32)
z=(float*)_mm_malloc(m*sizeof(float),32);
q=(float*)_mm_malloc(m*sizeof(float),32);
}
~charge() { _mm_free(x); _mm_free(y);
_mm_free(z); _mm_free(q);
}
33
};
Ejemplo
__declspec(align(32)) float A[n];
X=(float*)_mm_malloc(size, 32);
__atribute__ ((aligned (base,offset))) float A[n];
Y avisar al compilador
•
}
En vez de un vector de estructuras, una
estructura de vectores.
#pragma vector aligned
__assume_aligned(array, n)
32
Ejemplo
void calculate_electric_potential(
const int m, // N. of charges
const int n, // N. of points in each dim.
const Charge &chg, // Charges
float* restrict const phi, // Potential
const float ds // Spatial grid spacing
)
34
Ejemplo: Rendimiento
for (int c=0; c<n*n ; c++) {
const float Rx=ds*(float)(c/n);//X-coord
const float Ry=ds*(float)(c%n);//Y-coord
const float Rz=ds*(float)(0);//Z-coord
#pragma vector aligned
f
for
(i
(int
t i
i=0;
0 i<
i<m; i++){// autovectorized
t
t i d
const float dx=chg.x[i] - Rx;
const float dy=chg.y[i] - Ry;
const float dz=chg.y[i] - Rz;
phi[c] -=chg.q[i]/sqrtf(dx*dx+dy*dy+dz*dz);
}
}
35
36
6