2010-08-11 15 views
12

Tengo una estructura llamada log que tiene 13 caracteres. después de hacer un tamaño de (log) veo que el tamaño no es 13 sino 16. Puedo usar __attribute __ ((packed)) para obtener el tamaño real de 13, pero me pregunto si esto afectará el rendimiento del programa. Es una estructura que se usa con bastante frecuencia.¿Puede __tribuir __ ((empaquetado)) afectar el rendimiento de un programa?

Me gustaría poder leer el tamaño de la estructura (13 no 16). Podría usar una macro, pero si esta estructura cambia alguna vez, es decir, campos agregados o eliminados, me gustaría que el nuevo tamaño se actualice sin cambiar una macro porque creo que es propenso a errores. ¿Tienes alguna sugerencia?

Respuesta

15

Sí, afectará el rendimiento del programa. Agregar el relleno significa que el compilador puede usar instrucciones de carga enteras para leer cosas de la memoria. Sin el relleno, el compilador debe cargar cosas por separado y hacer cambios de bits para obtener el valor completo. (Incluso si es x86 y esto lo hace el hardware, todavía tiene que hacerse).

Considere esto: ¿Por qué los compiladores insertarían espacio aleatorio sin usar si no fuera por motivos de rendimiento?

+5

La mayoría del hardware maneja la mayoría de las cargas desalineadas sin una penalización. La excepción a la regla es cuando el acceso abarca algún tipo de límite: línea de caché, página, etc. Las instrucciones de mención son engañosas. En particular, si el conjunto de trabajo no encaja en el caché (no es una situación inusual), el beneficio de menos transacciones de DRAM para un conjunto "comprimido" probablemente superará los accesos de caché adicionales. Doblemente para las estructuras escritas en el disco. – Potatoswatter

+4

@Potatoswatter: "la mayoría"? Tal vez si "la mayoría de las máquinas son x86" su afirmación tiene alguna posibilidad de ser cierta, pero la última vez que verifiqué la mayoría de las máquinas son sistemas embebidos, teléfonos celulares, etc. En la mayoría de hardware, el acceso no alineado significa que el compilador debe generar código que realiza las cargas/almacena byte por byte, posiblemente con bitshifting y bitwise o para ensamblar valores, para trabajar con tipos más grandes. Esta es una gran penalización. –

+0

@R: ARM pre-ARMv6 no admite desalineación, según Wikipedia. Aparte de eso, SPARC y DSP, la mayoría de las arquitecturas lo admiten. De todos modos, incluso el volcado de bytes tedioso realizado a la velocidad de la CPU puede no ser más lento que el tiempo de transferencia extra de disco/flash/DRAM. – Potatoswatter

5

Sí, puede afectar el rendimiento. En este caso, si asigna una matriz de tales estructuras con el atributo ((packed)), la mayoría de ellas debe terminar desalineada (mientras que si usa el empaque predeterminado, todas pueden alinearse en límites de 16 bytes). Copiar tales estructuras puede ser más rápido si están alineadas.

5

Sí, puede afectar el rendimiento. Cómo depende de qué es y cómo lo usa.

Una variable desalineada posiblemente puede colocar dos líneas de caché. Por ejemplo, si tiene líneas de caché de 64 bytes y lee una variable de 4 bytes de una matriz de estructuras de 13 bytes, hay una probabilidad de 3 en 64 (4,6%) de que se distribuya en dos líneas. La penalización de un acceso de caché adicional es bastante pequeña. Si todo lo que hizo su programa fue golpear esa variable, el 4,6% sería el límite superior del golpe de rendimiento. Si el registro representa el 20% de la carga de trabajo del programa, y ​​leer/escribir en esa estructura es el 50% del registro, entonces ya está en una pequeña fracción de un porcentaje.

Por otro lado, presumiendo que el registro necesita ser guardado, la reducción de cada registro en 3 bytes te ahorra el 19%, lo que se traduce en una gran cantidad de memoria o espacio en el disco. La memoria principal y especialmente el disco son lentos, por lo que probablemente será mejor que empaques el registro para reducir su tamaño.


En cuanto a la lectura del tamaño de la estructura sin preocuparse por el cambio de la estructura, usar sizeof. Sin embargo, le gustaría hacer constantes numéricas, ya sea const int, enum o #define, simplemente agregue sizeof.

+0

Crear grabaciones mediante estructuras de escritura probablemente no sea una buena idea en primer lugar: pasar a un compilador o plataforma diferente haría que las grabaciones anteriores no tuvieran ningún valor. –

+0

@Billy: el argumento también se aplica a la DRAM lenta ("memoria principal") no escrita en el disco. De todos modos, la serialización adecuada simplemente requiere la conversión a una endianidad estándar. – Potatoswatter

+0

Y asegurando que los tamaños de los tipos en su estructura no pueden cambiar. –

6

No utilice __attribute__((packed)). Si su estructura de datos está en la memoria, permita que ocupe su tamaño natural según lo determine el compilador. Si es para leer/escribir desde/hacia el disco, escriba funciones de serialización y deserialización; no simplemente almacene estructuras binarias nativas de cpu en el disco. Las estructuras "empaquetadas" realmente tienen no usos legítimos (o muy pocos, vea los comentarios sobre esta respuesta para posibles puntos de vista en desacuerdo).

+2

Hay otras situaciones en las que tiene que lidiar con estructuras de datos organizadas bit por bit. Por ejemplo, la mayoría de los dispositivos SPI o I2C toman bytes de datos con una estructura muy específica. Dada la elección entre 20 o más operaciones de enmascaramiento y cambio de bit, o una estructura de datos documentada y empaquetada y un tipo de juego bien definido, tomaría esto último. – detly

+2

Sugeriría que el mapeo de estructuras en registros de hardware es un uso legítimo en sistemas integrados, por ejemplo. – jcoder

+0

Me gustaría agrupar estos usos con escrituras en el disco, como "serialización". Es cuestionable si el compilador con '__attribute __ ((packed))' generaría un código mejor de lo que podría hacer a mano con macros, y el último sería portable (a otras implementaciones C en el mismo hardware), pero concederé que este es un lugar en el que podría tener sentido usar una extensión de compilador como esta. –

-1

Al igual que con todas las otras optimizaciones de rendimiento, tendrá que perfilar su código para encontrar la respuesta correcta. La respuesta correcta variará según la arquitectura, y cómo usas tu estructura.

Si está creando matrices gigantescas, el ahorro de espacio del embalaje puede significar la diferencia entre el ajuste y el ajuste en la memoria caché.O es posible que sus datos ya se ajusten a su caché, en cuyo caso no hará ninguna diferencia. Si está asignando grandes cantidades de estructuras en un contenedor asociativo STL que asigna el almacenamiento para su estructura con operator new, puede no importar nada --- operator new podría redondear su almacenamiento a algo que esté alineado de todos modos.

Si la mayoría de sus estructuras viven en la pila, el almacenamiento extra podría estar optimizado de todos modos.

Para un cambio tan simple de probar, sugiero construir una plataforma de tiempo y luego probar cosas en ambos sentidos. Para optimizaciones adicionales, sugiero usar un generador de perfiles para identificar sus cuellos de botella e ir desde allí.

Cuestiones relacionadas