2011-10-17 12 views
10

Nuestros encabezados usan #pragma pack(1) alrededor de la mayoría de nuestras estructuras (se usan para red y archivo de E/S). Entiendo que cambia la alineación de las estructuras del valor predeterminado de 8 bytes a una alineación de 1 byte.¿Hay problemas de rendimiento al usar pragma pack (1)?

Suponiendo que todo se ejecuta en Linux de 32 bits (tal vez Windows también), ¿hay algún impacto en el rendimiento que proviene de esta alineación de empaque?

No me preocupa la portabilidad de las bibliotecas, pero más con la compatibilidad de E/S de archivos y redes con diferentes paquetes #pragma y problemas de rendimiento.

+0

Ni siquiera sabía que GCC es compatible con '#pragma pack'. No es que vaya a usarlo ahora. –

+0

@larsmans Sí lo hace, debido a Windows: http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html – Nicolas

Respuesta

11

El acceso a memoria es más rápido cuando puede tener lugar en direcciones de memoria alineadas con palabras. El ejemplo más simple es el siguiente struct (que @Didier también se utiliza):

struct sample { 
    char a; 
    int b; 
}; 

Por defecto, GCC inserta relleno, por lo que una es en la posición 0, y b está en el offset 4 (palabra Alineados). Sin relleno, b no está alineado con la palabra, y el acceso es más lento.

¿Cuánto más lento?

  • Para x86 de 32 bits, de acuerdo con la Intel 64 and IA32 Architectures Software Developer's Manual:
    El procesador requiere dos memoria accesos a hacer un acceso a la memoria no alineada; los accesos alineados requieren solo un acceso a la memoria . Un operando de palabra o palabra doble que cruza un límite de 4 bytes o un operando de cuatro palabras que cruza un límite de 8 bytes se considera desalineado y requiere dos ciclos de bus de memoria separados para el acceso.
    Al igual que con la mayoría de las preguntas de rendimiento, tendría que comparar su aplicación para ver cuánto de esto es en la práctica.
  • Según Wikipedia, las extensiones x86 como SSE2 requieren alineación de palabras.
  • Muchas otras arquitecturas requieren la alineación de palabras (y generarán errores de SIGBUS si las estructuras de datos no están alineadas con la palabra).

En cuanto a la portabilidad: Asumo que está utilizando #pragma pack(1) por lo que se puede enviar a través del cable estructuras y hacia y desde el disco sin tener que preocuparse acerca de las diferentes plataformas compiladores o estructuras de embalaje de manera diferente. Esto es válido, sin embargo, hay un par de cuestiones a tener en cuenta:

  • Esto no hace nada para manejar big endian frente a pequeños problemas endian. Puede manejar estos llamando a la familia de funciones htons en cualquier entrada, sin signo, etc. en sus estructuras.
  • En mi experiencia, trabajar con estructuras empaquetables y serializables en el código de la aplicación no es muy divertido. Son muy difíciles de modificar y ampliar sin romper la compatibilidad hacia atrás, y como ya se señaló, existen penalizaciones de rendimiento. Considere la posibilidad de transferir los contenidos de sus estructuras empaquetables y serializables a estructuras equivalentes no empaquetadas y extensibles para su procesamiento, o considere el uso de una biblioteca de serialización completa como Protocol Buffers (que tiene C bindings).
+1

+1 para una respuesta excelente y para señalar que algunas arquitecturas que no son x86 en realidad * requieren * una alineación adecuada para ciertos tipos de datos. –

+0

Endinaness en realidad no se maneja, pero está "OK" ya que todo nuestro backoffice es Linux. De hecho, ejecutaré un punto de referencia y tal vez lo informe aquí. Gracias por la respuesta. – Nicolas

3

Cuando declara una estructura, la mayoría de los compiladores insertan bytes de relleno entre los miembros para asegurarse de que estén alineados con las direcciones apropiadas en la memoria (generalmente los bytes de relleno son un múltiplo del tamaño del tipo). Esto permite que el compilador tenga un acceso optimizado para obtener estos miembros.

#pragma pack(1) indica al compilador que empaquete los miembros de la estructura con una alineación particular. El 1 aquí le dice al compilador que no inserte ningún relleno entre los miembros.

Así Sí, hay una penalización en el rendimiento definitivo, ya que se fuerza al compilador que hacer algo más allá de lo que lo haría de forma natural para optimization.Also rendimiento, algunas plataformas exigencia de que los objetos estén alineados en los límites específicos y el uso de unalighed las estructuras pueden darte fallas de segmentación.

Idealmente, es mejor evitar cambiar las reglas de alineación natural predeterminadas. Pero si la directiva 'pragma pack' no se puede evitar en absoluto (como en su caso), entonces el esquema de empaque original debe restaurarse después de la definición de las estructuras que requieren un empaque ajustado.

Por ejemplo:

//push current alignment rules to internal stack and force 1-byte alignment boundary 
#pragma pack(push,1) 

/* definition of structures that require tight packing go in here */ 

//restore original alignment rules from stack  
#pragma pack(pop) 
+1

O mejor, use el atributo nativo ['aligned' de gcc] (http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Type-Attributes.html) para marcar solo la estructura actual. –

2

Depende de la arquitectura subyacente y la forma en que maneja las direcciones no alineadas.

x86 maneja las direcciones sin alinear correctamente, aunque a un costo de rendimiento, mientras que otras arquitecturas como ARM pueden invocar un error de alineación (SIGBUS), o incluso "redondear" la dirección desalineada al límite más cercano, en cuyo caso su código fallar de una manera horrible

En pocas palabras, empaquételo solo si estás seguro de que la arquitectura subyacente manejará las direcciones no alineadas, y si el costo de la E/S de red es mayor que el costo de procesamiento.

+0

De hecho es x86. – Nicolas

+0

¿Cuál es su sugerencia? Si los datos se envían entre un ARM y una máquina X86, ¿qué formato de paquete debo usar? – Benny

6

Sí. Absolutamente hay.

Por ejemplo, si se define una estructura:

struct dumb { 
    char c; 
    int i; 
}; 

entonces cada vez que acceda al miembro i, se ralentiza la CPU, ya que el valor de los 32 bits i no es accesible de forma nativa, alineado. Para hacerlo simple, imagine que la CPU tiene que obtener 3 bytes de la memoria, y luego 1 byte adicional de la siguiente ubicación para transferir el valor de la memoria a los registros de la CPU.

0

Técnicamente, sí, afectaría el rendimiento, pero solo en lo que respecta al procesamiento interno. Si necesita las estructuras empaquetadas para red/archivo IO, hay un equilibrio entre el requisito de empaquetado y el procesamiento interno. Por procesamiento interno, quiero decir, el trabajo que haces en los datos entre el IO.Si hace muy poco procesamiento, no perderá mucho en términos de rendimiento. De lo contrario, es posible que desee hacer un procesamiento interno en estructuras alineadas correctamente y solo "empacar" los resultados al hacer IO. O bien, podría cambiar a usar estructuras alineadas por defecto, pero deberá asegurarse de que todos los alineen de la misma manera (clientes de red y de archivos).

0

Existen ciertas instrucciones de código de máquina que operan en 32 bit o 64 bit (o incluso más) pero esperan que los datos se alineen en las direcciones de memoria. Si no lo son, tienen que hacer más de un ciclo de lectura/escritura en memoria para realizar su tarea. El rendimiento del rendimiento depende en gran medida de lo que esté haciendo con los datos. Si construyes grandes conjuntos de estructuras y realizas grandes cálculos sobre ellas, puede llegar a ser grande. Pero si solo almacena los datos una vez solo para volver a leerlos en otro momento, convirtiéndolos en una transmisión de bytes de todos modos, es posible que apenas se note.

Cuestiones relacionadas