Dígale a C++ que los datos del puntero están alineados a 16 bytes
gcc sse (1)
Escribí un código con matrices estáticas y se vectoriza muy bien.
float data[1024] __attribute__((aligned(16)));
Me gustaría hacer los arreglos dinámicamente asignados. Intenté hacer algo como esto:
float *data = (float*) aligned_alloc(16, size*sizeof(float));
Pero el compilador (GCC 4.9.2), ya no puede vectorizar el código. Supongo que esto se debe a que no sabe que los datos del puntero están alineados a 16 bytes. Estoy recibiendo mensajes como:
note: Unknown alignment for access: *_43
He intentado agregar esta línea antes de que se utilicen los datos, pero no parece hacer nada:
data = (float*) __builtin_assume_aligned(data, 16);
Usar una variable diferente y restrict
no ayudó:
float* __restrict__ align_data = (float*) __builtin_assume_aligned(data,16);
Ejemplo:
#include <iostream>
#include <stdlib.h>
#include <math.h>
#define SIZE 1024
#define DYNAMIC 0
#define A16 __attribute__((aligned(16)))
#define DA16 (float*) aligned_alloc(16, size*sizeof(float))
class Test{
public:
int size;
#if DYNAMIC
float *pos;
float *vel;
float *alpha;
float *k_inv;
float *osc_sin;
float *osc_cos;
float *dosc1;
float *dosc2;
#else
float pos[SIZE] A16;
float vel[SIZE] A16;
float alpha[SIZE] A16;
float k_inv[SIZE] A16;
float osc_sin[SIZE] A16;
float osc_cos[SIZE] A16;
float dosc1[SIZE] A16;
float dosc2[SIZE] A16;
#endif
Test(int arr_size){
size = arr_size;
#if DYNAMIC
pos = DA16;
vel = DA16;
alpha = DA16;
k_inv = DA16;
osc_sin = DA16;
osc_cos = DA16;
dosc1 = DA16;
dosc2 = DA16;
#endif
}
void compute(){
for (int i=0; i<size; i++){
float lambda = .67891*k_inv[i],
omega = (.89 - 2*alpha[i]*lambda)*k_inv[i],
diff2 = pos[i] - omega,
diff1 = vel[i] - lambda + alpha[i]*diff2;
pos[i] = osc_sin[i]*diff1 + osc_cos[i]*diff2 + lambda*.008 + omega;
vel[i] = dosc1[i]*diff1 - dosc2[i]*diff2 + lambda;
}
}
};
int main(int argc, char** argv){
Test t(SIZE);
t.compute();
std::cout << t.pos[10] << std::endl;
std::cout << t.vel[10] << std::endl;
}
Así es como estoy compilando:
g++ -o test test.cpp -O3 -march=native -ffast-math -fopt-info-optimized
Cuando DYNAMIC
se establece en 0
, genera:
test.cpp:46:4: note: loop vectorized
pero cuando se establece en 1
no produce nada.
El compilador no vectoriza el bucle porque no puede determinar que los punteros asignados dinámicamente no se aliasen entre sí. Una forma sencilla de permitir que su código de ejemplo se vectorice es pasar la --param vect-max-version-for-alias-checks=1000
. Esto permitirá al compilador emitir todas las comprobaciones necesarias para ver si los punteros son realmente alias.
Otra solución simple para permitir que su código de ejemplo se vectorice es cambiar el nombre de main
, como lo sugirió Marc Glisse en su comentario. Las funciones llamadas main
aparentemente tienen ciertas optimizaciones deshabilitadas. Nombrado de otra manera, GCC 4.9.2 puede rastrear el uso de this->foo
(y los otros miembros del puntero) en el compute
de sus asignaciones en Test()
.
Sin embargo, supongo que algo distinto a su clase que se usó en una función llamada main
impidió que su código se vectorizara en su código real. Una solución más general que permite que su código se vectorice sin aliasing o verificaciones de alineación es usar la palabra clave restrict
y el atributo aligned
. Algo como esto:
typedef float __attribute__((aligned(16))) float_a16;
__attribute__((noinline))
static void _compute(float_a16 * __restrict__ pos,
float_a16 * __restrict__ vel,
float_a16 * __restrict__ alpha,
float_a16 * __restrict__ k_inv,
float_a16 * __restrict__ osc_sin,
float_a16 * __restrict__ osc_cos,
float_a16 * __restrict__ dosc1,
float_a16 * __restrict__ dosc2,
int size) {
for (int i=0; i<size; i++){
float lambda = .67891*k_inv[i],
omega = (.89 - 2*alpha[i]*lambda)*k_inv[i],
diff2 = pos[i] - omega,
diff1 = vel[i] - lambda + alpha[i]*diff2;
pos[i] = osc_sin[i]*diff1 + osc_cos[i]*diff2 + lambda*.008 + omega;
vel[i] = dosc1[i]*diff1 - dosc2[i]*diff2 + lambda;
}
}
void compute() {
_compute(pos, vel, alpha, k_inv, osc_sin, osc_cos, dosc1, dosc2,
size);
}
El atributo noinline
es crítico, de lo contrario, la alineación puede hacer que los punteros pierdan su restricción y alineación. El compilador parece ignorar la palabra clave de restrict
en contextos distintos a los parámetros de la función.