¿Cómo decirle a GCC que un argumento de puntero siempre está alineado con dos palabras?

8 minutos de lectura

avatar de usuario
ysap

En mi programa tengo una función que hace una simple suma de vectores c[0:15] = a[0:15] + b[0:15]. El prototipo de función es:

void vecadd(float * restrict a, float * restrict b, float * restrict c);

En nuestra arquitectura integrada de 32 bits hay una opción de carga/almacenamiento para cargar/almacenar palabras dobles, como:

r16 = 0x4000  ;
strd r0,[r16] ; stores r0 in [0x4000] and r1 in [0x4004]

El optimizador GCC reconoce la naturaleza vectorial del bucle y genera dos ramas del código: una para el caso en que las 3 matrices están alineadas con palabras dobles (por lo que utiliza las instrucciones de carga/almacenamiento dobles) y la otra para el caso en que las matrices están alineados con palabras (donde usa la opción de carga/almacenamiento único).

El problema es que la verificación de alineación de direcciones es costosa en relación con la parte de suma y quiero eliminarla insinuando al compilador que a, b y c siempre están alineados en 8. ¿Hay algún modificador para agregar a la declaración del puntero para decirle esto al compilador?

Las matrices que se utilizan para llamar a esta función tienen el atributo alineado (8), pero no se refleja en el código de la función en sí. ¿Es posible agregar este atributo a los parámetros de la función?

  • Incluso si mi código a continuación no puede ayudarlo (debido a que es C ++), es posible que desee imprimir f (“% p”) & matriz[0] y &matriz[1] en su código solo para asegurarse de que se obedezca la alineación, y por elemento, no solo en la dirección de inicio de la matriz.

    – José

    07/03/2012 a las 20:50


  • @Joe: en realidad se requiere que NO se alinee por elemento de matriz. Realmente tiene que ser una matriz contigua de flotantes, cuyo origen esté alineado en 8.

    – ysap

    7 de marzo de 2012 a las 21:48

avatar de usuario
ams

Si los atributos no funcionan, o no son una opción….

No estoy seguro, pero prueba esto:

void vecadd (float * restrict a, float * restrict b, float * restrict c)
{
   a = __builtin_assume_aligned (a, 8);
   b = __builtin_assume_aligned (b, 8);
   c = __builtin_assume_aligned (c, 8);

   for ....

Eso debería decirle a GCC que los punteros están alineados. A partir de eso, si hace lo que quiere depende de si el compilador puede usar esa información de manera efectiva; puede que no sea lo suficientemente inteligente: estas optimizaciones no son fáciles.

Otra opción podría ser envolver el flotador dentro de una unión que debe estar alineada en 8 bytes:

typedef union {
  float f;
  long long dummy;
} aligned_float;

void vedadd (aligned_float * a, ......

Creo que eso debería hacer cumplir la alineación de 8 bytes, pero nuevamente, no sé si el compilador es lo suficientemente inteligente como para usarlo.

  • ¡Do! Acabo de notar en la página siguiente del manual de GCC __builtin_assume_aligned. Editaré la respuesta.

    – ams

    8 de marzo de 2012 a las 14:02

  • Gracias, @ams. Esta es probablemente la solución perfecta. Desafortunadamente, en nuestro compilador, aunque compilado bien, no afecta la salida y el compilador aún verifica si los punteros están alineados o no y elige la ruta de código requerida.

    – ysap

    8 de marzo de 2012 a las 17:09

  • Si alguien puede confirmar que funciona en otras arquitecturas, aceptaré esta respuesta.

    – ysap

    8 de marzo de 2012 a las 17:10

  • Su propuesta de unión, por cierto, no es lo que estoy buscando, ya que hará que todos los elementos de la matriz estén alineados en 8, mientras que estoy tratando con una matriz de flotadores. Sin embargo, puedo empaquetar dos flotadores en una estructura para que funcionen de la misma manera.

    – ysap

    8 de marzo de 2012 a las 17:13

  • Lanzar cosas es probablemente un mal plan, ya que puede obtener errores de alias. No hay razón por la que no puedas tener union {float f[100]; long long dummy} aunque 🙂

    – ams

    8 de marzo de 2012 a las 19:40

avatar de usuario
ysap

Siguiendo un fragmento de código de ejemplo que encontré en mi sistema, probé la siguiente solución, que incorpora ideas de algunas de las respuestas dadas anteriormente: básicamente, crear una unión de una pequeña matriz de flotadores con un tipo de 64 bits: en este caso, un vector SIMD de flotadores, y llame a la función con una conversión de las matrices de flotadores de operandos:

typedef float f2 __attribute__((vector_size(8)));
typedef union { f2 v; float f[2]; } simdfu;

void vecadd(f2 * restrict a, f2 * restrict b, f2 * restrict c);

float a[16] __attribute__((aligned(8)));
float b[16] __attribute__((aligned(8)));
float c[16] __attribute__((aligned(8)));

int main()
{
    vecadd((f2 *) a, (f2 *) b, (f2 *) c);
    return 0;
}

Ahora el compilador no genera la rama alineada en 4.

sin embargo, el __builtin_assume_aligned() sería la solución preferible, previniendo el yeso y posibles efectos secundarios, si tan solo funcionara…

EDITAR: noté que la función incorporada tiene errores en nuestra implementación (es decir, no solo no funciona, sino que causa errores de cálculo más adelante en el código).

¿Cómo decirle a GCC que un argumento de puntero siempre está alineado con dos palabras?

Parece que las versiones más nuevas de GCC tienen __builtin_assume_aligned:

Función incorporada: void * __builtin_assume_aligned (const void *exp, size_t align, ...)

Esta función devuelve su primer argumento y permite que el compilador asuma que el puntero devuelto tiene al menos align bytes alineados. Este incorporado puede tener dos o tres argumentos, si tiene tres, el tercer argumento debe ser de tipo entero, y si es distinto de cero significa compensación de desalineación. Por ejemplo:

void *x = __builtin_assume_aligned (arg, 16);

significa que el compilador puede suponer que x, establecido en arg, tiene una alineación de al menos 16 bytes, mientras que:

void *x = __builtin_assume_aligned (arg, 32, 8);

significa que el compilador puede suponer para x, establecido en arg, que (char *) x – 8 está alineado en 32 bytes.

Según algunas otras preguntas y respuestas sobre Stack Overflow alrededor de 2010, parece que el integrado no estaba disponible en GCC 3 y GCC 4 temprano. Pero no sé dónde está el punto de corte.

  • Gracias. Se mencionó en un par de las respuestas aquí, y estaba disponible en GCC en el momento de hacer la pregunta.

    – ysap

    27/03/2017 a las 10:00

Las versiones de gcc han sido dudosas acerca de align() en matrices y definiciones de tipos simples. Por lo general, para hacer lo que desea, tendría que envolver el flotador en una estructura y hacer que el flotador contenido tenga la restricción de alineación.

Con la sobrecarga de operadores, casi puede hacer que esto sea sencillo, pero se supone que puede usar la sintaxis de c ++.

#include <stdio.h>
#include <string.h>

#define restrict __restrict__

typedef float oldfloat8 __attribute__ ((aligned(8)));

struct float8
{
    float f __attribute__ ((aligned(8)));

    float8 &operator=(float _f) { f = _f; return *this; }
    float8 &operator=(double _f) { f = _f; return *this; }
    float8 &operator=(int _f) { f = _f; return *this; }

    operator float() { return f; }
};

int Myfunc(float8 * restrict a, float8 * restrict b, float8 * restrict c);

int MyFunc(float8 * restrict a, float8 * restrict b, float8 * restrict c)
{
    return *c = *a* *b;
}

int main(int argc, char **argv)
{
    float8 a, b, c;

    float8 p[4];

    printf("sizeof(oldfloat8) == %d\n", (int)sizeof(oldfloat8));
    printf("sizeof(float8) == %d\n", (int)sizeof(float8));

    printf("addr p[0] == %p\n", &p[0] );
    printf("addr p[1] == %p\n", &p[1] );

    a = 2.0;
    b = 7.0;
    MyFunc( &a, &b, &c );
    return 0;
}

Las especificaciones de alineación generalmente solo funcionan para alineaciones que son más pequeñas que el tipo base de un puntero, no más grandes.

Creo que lo más fácil es declarar toda la matriz con una especificación de alineación, algo así como

typedef float myvector[16];
typedef myvector alignedVector __attribute__((aligned (8));

(La sintaxis puede no ser correcta, siempre tengo dificultades para saber dónde poner estos __attribute__s)

Y use ese tipo en todo su código. Para su definición de función, intentaría

void vecadd(alignedVector * restrict a, alignedVector * restrict b, alignedVector * restrict c);

Esto le da una indirección adicional, pero esto es solo sintaxis. Algo como *a es solo un noop y solo reinterpreta el puntero como un puntero al primer elemento.

  • Gracias. ¿Por qué no poner el atributo en el 1er typedef?

    – ysap

    07/03/2012 a las 21:40

  • @ysap, simplemente no sé dónde colocar el atributo para un tipo de matriz. La sintaxis es cruda.

    – Jens Gusted

    7 de marzo de 2012 a las 21:47

  • Siguiendo el comentario de Joe, creo que la respuesta a mi pregunta anterior es que atribuir el primer typedef hará que la matriz elementos 8-alineado, y no la matriz en sí (pero obviamente, voluntad estar alineado debido a la alineación del 1er elemento). ¿Tiene sentido?

    – ysap

    07/03/2012 a las 21:51


avatar de usuario
Jörg Beyer

Nunca lo usé, pero hay _atributo_((alineado (8)))

Si leo bien la documentación, entonces se usa de esta manera:

void vecadd(float * restrict a __attribute__((aligned (8))), 
            float * restrict b __attribute__((aligned (8))), 
            float * restrict c __attribute__((aligned (8))));

ver http://ohse.de/uwe/articles/gcc-attributes.html#type-aligned

  • Gracias. ¿Por qué no poner el atributo en el 1er typedef?

    – ysap

    07/03/2012 a las 21:40

  • @ysap, simplemente no sé dónde colocar el atributo para un tipo de matriz. La sintaxis es cruda.

    – Jens Gusted

    7 de marzo de 2012 a las 21:47

  • Siguiendo el comentario de Joe, creo que la respuesta a mi pregunta anterior es que atribuir el primer typedef hará que la matriz elementos 8-alineado, y no la matriz en sí (pero obviamente, voluntad estar alineado debido a la alineación del 1er elemento). ¿Tiene sentido?

    – ysap

    07/03/2012 a las 21:51


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad