2012-06-19 30 views
8

Después de la discusión de this post, he entendido que la razón principal para la alineación de los miembros de la estructura es el rendimiento (y algunas restricciones de las arquitecturas).Doble alineación

Si investigaremos Microsoft (Visual C++), Borland/CodeGear (C++ - Builder), Mars Digital (DMC) y GNU (GCC) al compilar para x86 de 32 bits: La alineación para int es de 4 bytes y si int no está alineado, puede suceder que se lean 2 filas de bancos de memoria.

Mi pregunta es por qué no hacer que double tenga 4 bytes alineados también? 4 bytes alineados double también causará 2 filas de bancos de memoria de lectura ....

Por ejemplo, en el siguiente ejemplo, ya que double es 8 Alineados, el tamaño real de la estructura será sizeof(char) + (alignment for double padding) + sizeof(int) = 20 bytes.

typedef struct structc_tag{ 
    char  c; 
    double  d; 
    int   s; 
} structc_t; 

Gracias

+2

Alineación es la implementación definida, por lo que tiene poco sentido hacer esta pregunta de manera general. Debe elegir un compilador/plataforma específico para preguntar. –

+0

En realidad obtengo 24 bytes ... –

+1

También hay 4 bytes de relleno al final para asegurar que el doble aún esté alineado en los elementos de una matriz de estas estructuras. Generalmente. –

Respuesta

9

un comentario extendida:

de acuerdo con documentación de GCC sobre -malign-double:

Alineación de double las variables en una de dos palabras Boundary produce un código que se ejecuta un poco más rápido en un Pentium a expensas de más memoria.

En x86-64, -malign-double está habilitado de manera predeterminada.

Advertencia: si usa el conmutador -malign-double, las estructuras que contienen los tipos anteriores están alineadas de forma diferente a las especificaciones de interfaz binaria de la aplicación publicadas para el 386 y no son compatibles con estructuras en código compilado sin ese conmutador.

Una palabra aquí significa i386 palabra que es de 32 bits.

Windows usa la alineación de 64 bits de los valores double incluso en modo de 32 bits, mientras que los Unices SysV i386 ABI usan alineación de 32 bits. La API de Windows de 32 bits, Win32, proviene de Windows NT 3.1, que, a diferencia de las versiones de Windows de la generación actual, tenía como objetivo Intel i386, Alpha, MIPS e incluso el oscuro Intel i860. Como los sistemas RISC nativos como Alpha y MIPS requieren que los valores double estén alineados en 64 bits (de lo contrario, se produce un error de hardware), la portabilidad podría haber sido la razón detrás de la alineación de 64 bits en Win32 i386 ABI.

Los sistemas de 64 bits x86, conocidos también como AMD64 o x86-64, o x64, requieren valores de double para estar alineados a 64 bits; de lo contrario, se produce un error de alineación y el hardware realiza una costosa "reparación" que ralentiza considerablemente acceso a memoria reducida. Es por eso que los valores double están alineados en 64 bits en todos los ABI x86-64 modernos (SysV y Win32).

0

La pregunta es altamente específica de la plataforma en cuanto a la arquitectura de la CPU. Por ejemplo, en arquitecturas que otorgan penalizaciones por operar en direcciones no alineadas en 4 bytes, alinear sus variables (de hecho sus direcciones) a 4 bytes puede evitar dicha penalización.

Los compiladores son bastante buenos con este tipo de cosas, especialmente cuando se les proporciona la arquitectura de CPU de destino para la cual se optimizan para que puedan hacer la mayor parte de esto, y muchas otras optimizaciones también. Eche un vistazo a la bandera -march de GCC, por ejemplo, que le permite apuntar arquitecturas de CPU.

+0

Seré más específico :) - Quiero entender el motivo del diseño para que dichas plataformas realicen una doble alineación de 8 bytes – Yakov

6

La mayoría de los compiladores alinearán automáticamente los valores de datos al tamaño de palabra de la plataforma, o al tamaño del tipo de datos, el que sea más pequeño. La gran mayoría de los procesadores de consumo y empresariales usan un tamaño de palabra de 32 bits. (Incluso los sistemas de 64 bits generalmente usan 32 bits como tamaño de palabra nativo)

Como tal, el orden de los miembros en su estructura podría desperdiciar algo de memoria. En tu caso específico, estás bien. Voy a añadir en los comentarios de la huella real de memoria utilizada:

typedef struct structc_tag{ 
      char  c; // 1 byte 
         // 3 bytes (padding) 
      double  d; // 8 bytes 
      int   s; // 4 bytes 
} structc_t;    // total: 16 bytes 

Esta regla se aplica a estructuras demasiado, por lo que incluso si los reorganizado por lo que el campo más pequeño fue el último, a pesar de ello tiene una estructura del mismo tamaño (16 bytes).

typedef struct structc_tag{ 
      double  d; // 8 bytes 
      int   s; // 4 bytes 
      char  c; // 1 byte 
         // 3 bytes (padding) 
} structc_t;    // total: 16 bytes 

Si se va a declarar más campos que eran más pequeños de 4 bytes, se podía ver algunas reducciones de memoria si se agrupan por tamaño. Por ejemplo:

typedef struct structc_tag{ 
      double  d1; // 8 bytes 
      double  d2; // 8 bytes 
      double  d3; // 8 bytes 
      int   s1; // 4 bytes 
      int   s2; // 4 bytes 
      int   s3; // 4 bytes 
      short  s4; // 2 bytes 
      short  s5; // 2 bytes 
      short  s6; // 2 bytes 
      char  c1; // 1 byte 
      char  c2; // 1 byte 
      char  c3; // 1 byte 
          // 3 bytes (padding) 
} structc_t;    // total: 48 bytes 

Declarar una estructura estúpida podría perder una gran cantidad de memoria, a no ser que el compilador reordena los elementos (que, en general, no lo hará, sin ser explícitamente dicho a)

typedef struct structc_tag{ 
      int   s1; // 4 bytes 
      char  c1; // 1 byte 
          // 3 bytes (padding) 
      int   s2; // 4 bytes 
      char  c2; // 1 byte 
          // 3 bytes (padding) 
      int   s3; // 4 bytes 
      char  c3; // 1 byte 
          // 3 bytes (padding) 
} structc_t;    // total: 24 bytes 
          // (9 bytes wasted, or 38%) 
          // (optimal size: 16 bytes (1 byte wasted)) 

Los dobles son mayores que 32 bits, y por lo tanto de acuerdo con la regla en la primera sección, están alineados a 32 bits. Alguien mencionó una opción del compilador que cambia la alineación, y que la opción del compilador predeterminado es diferente entre los sistemas de 32 y 64 bits, esto también es válido. Entonces, la verdadera respuesta sobre los dobles es que depende de la plataforma y el compilador.

El rendimiento de la memoria se rige por palabras: la carga desde la memoria ocurre en etapas que dependen de la ubicación de los datos. Si los datos cubren una palabra (i.mi. es palabra alineada), solo esa palabra necesita ser cargada. Si no está alineado correctamente (es decir, un int en 0x2), el procesador debe cargar 2 palabras para leer correctamente su valor. Lo mismo se aplica a los dobles, que normalmente ocupan 2 palabras, pero si no están alineados, retírenlos 3. En sistemas de 64 bits donde es posible la carga nativa de cantidades de 64 bits, se comportan como 32 bits en sistemas de 32 bits si están correctamente alineados , que se pueden recuperar en una sola pasada, pero por lo demás, requerirán 2.

+0

A pesar de que los ejemplos son agradables, su respuesta no responde a mi pregunta: Estoy buscando la razón por la que los diseñadores no t allign double by 4 bytes – Yakov

+0

Editaré mi respuesta para reflejar eso. – Wug

2

En primer lugar, es la arquitectura que impone el requisito de alineación, algunos tolerarán los accesos de memoria no alineados, otros no.

permite echar plataforma x86-32bit ventanas como un ejemplo, en esta plataforma con el requisito de alineación para int y long es 4 bytes y 8 bytes respectivamente.

Está claro por qué el requisito de alineación int es 4 bytes, simplemente para que la CPU pueda leerlo todo solo con un acceso.

La razón por la cual el requisito de alineación para doulbe es 8 bytes y no 4 bytes, es porque si fuera 4 bytes luego pensar en lo que sucederá si esta doble se encuentra en la dirección 60 y el tamaño de línea de caché era 64bits, en este caso el procesador necesita cargar 2 líneas de caché de la memoria a la caché, pero si este double estaba alineado, esto no sucederá, ya que en este caso el double siempre será parte de una línea de caché y no se dividirá entre dos.

...58 59|60 61 62 63 64 65 66 67|68 69 70 71... 
    - - - - - - - - - - - - - - - - - 
----------+ + + + . . + + + +-------------- 
      |   . .   | 
----------+ + + + . . + + + +-------------- 
         . .   
     Cache Line 1  . . Cache Line 2 
    - - - - - - - - - - - - - - - - -