Gestión de memoria (III)

Alineación (alignment)


El proceso de alineación de datos en memoria implica el alojamiento de variables de un tipo determinado en direcciones de memoria que sean múltiplos enteros de un cierto valor L (normalmente 1, 2, 4, 8, ó 16) [1]. Concretamente, sea D la dirección en memoria de una variable cualquiera. La alineación L de dicha variable (medida en bytes) se define como la mayor potencia de 2 tal que

D mod L = 0.

Con carácter general, el compilador GCC impone la siguiente tabla de alineaciones para tipos primitivos y punteros en un sistema Linux de 64 bits:

Tamaño (bytes) Alineación (bytes)
char 1 1
short 2 2
int 4 4
long int 4 4
long long int 8 8
float 4 4
double 8 8
long double 16* 16
Puntero a cualquier tipo 8 8
* En el caso de long double, sólo 10 bytes son realmente utilizados para la representación numérica (80 bits de precisión); los 6 bytes restantes son añadidos para garantizar una alineación que sea múltiplo entero del tamaño de una palabra [2]. 

La alineación de datos simplifica sustancialmente la comunicación entre la CPU y la memoria del computador. Así, por ejemplo, las variables de tipo primitivo double, de 8 bytes de tamaño, poseen una alineación L = 8, de modo que las operaciones de escritura/lectura de las mismas puedan realizarse mediante una simple operación de escritura/lectura de una palabra en memoria.

Dada una estructura o clase, el compilador puede introducir bloques de bytes inutilizados entre los datos miembros con el fin de garantizar la correcta alineación de todos ellos. Así, por ejemplo, consideremos el agregado

   struct S {       char a;       int i;       char b;    };

De entrada, la alineación de un objeto cualquiera de tipo S coincidirá con la del dato miembro de mayor alineación (en nuestro caso, el entero int con L = 4). Así, la dirección en memoria de un objeto de tipo S será siempre múltiplo entero de 4. El compilador optará, asimismo, por insertar 3 bytes entre el primer carácter a y el entero i, con el fin de garantizar una alineación igual a 4 para el entero. Finalmente, insertará un bloque de 3 bytes tras el segundo carácter b de manera que, de crearse un array de elementos de tipo S, cada elemento del mismo satisfaga los requerimientos de alineación mencionados anteriormente:

   struct S { // tras compilación       char a;       char __padding_1[3];       int i;       char b;       char __padding_2[3];    };


El tamaño de un objeto de tipo S es, pues, de 12 bytes. Por supuesto, una mera reordenación de los datos miembro de la estructura conseguiría un uso más eficiente de la memoria. Así, los objetos de la siguiente estructura ocuparían únicamente 8 bytes:

   struct S {       int i;       char a, b;    };


En el lenguaje C++, puede utilizarse el operador sizeof() para conocer el tamaño (medido en bytes) de un tipo cualquiera (primitivo o definido por el programador), mientras que el operador alignof() proporciona su alineación en memoria. A modo de ejemplo, y para la última definición del agregado S:

   std::cout << "size of S: " << sizeof(S) << " bytes\n"              << "alignment of S: " << alignof(S) << " bytes\n";    /* output:          size of S: 8 bytes          alignment of S: 4 bytes    */

El especificador alignas permite modificar la alineación natural de una variable o un dato miembro de una clase, siempre que no se debilite la alineación original de ésta. En el ejemplo siguiente, se introduce un búfer con una longitud dada de bytes con la máxima alineación posible (al menos tan estricta como la de cualquier tipo escalar, típicamente la de long double, es decir, 8 ó 16 bytes según la plataforma en que se trabaje):

   constexpr auto buffer_size = std::size_t{/* número de bytes */};    alignas(std::max_align_t) unsigned char buffer[buffer_size];


Referencias bibliográficas:
  1. Data alignment (definition) - https://docs.microsoft.com/en-us/cpp/cpp/alignment-cpp-declarations?view=vs-2019
  2. Size of long integer - https://software.intel.com/en-us/articles/size-of-long-integer-type-on-different-architecture-and-os

1 comentario: