¿Por qué GCC no optimiza las estructuras?

8 minutos de lectura

avatar de usuario
Alex Gartrell

Los sistemas exigen que ciertas primitivas se alineen con ciertos puntos dentro de la memoria (ints a bytes que son múltiplos de 4, shorts a bytes que son múltiplos de 2, etc.). Por supuesto, estos se pueden optimizar para desperdiciar el menor espacio en el relleno.

Mi pregunta es ¿por qué GCC no hace esto automáticamente? ¿Falta de alguna manera la heurística más obvia (variables de orden desde el requisito de tamaño más grande hasta el más pequeño)? ¿Algún código depende del ordenamiento físico de sus estructuras (es una buena idea)?

Solo pregunto porque GCC está súper optimizado de muchas maneras, pero no en esta, y creo que debe haber alguna explicación relativamente buena (de la cual no estoy al tanto).

  • Puedes probar el -fipa-struct-reorg opción en struct-reorg-branch. ¿Hay una palabra clave de GCC para permitir el reordenamiento de estructuras?

    – phuclv

    15 de julio de 2018 a las 15:06

avatar de usuario
Damián Neil

gcc no reordena los elementos de una estructura, porque eso violaría el estándar C. La sección 6.7.2.1 del estándar C99 establece:

Dentro de un objeto de estructura, los miembros que no son campos de bits y las unidades en las que residen los campos de bits tienen direcciones que aumentan en el orden en que se declaran.

  • Sí, pero ¿por qué se definió de esta manera?

    – nes1983

    1 de enero de 2014 a las 22:51

  • @nes1983 El programador puede estar haciendo suposiciones sobre el orden de los datos en la estructura y puede estar usando máscaras para obtener cada parte. Si la estructura se reordena, el enmascaramiento puede ser incorrecto.

    – Evo510

    2 de enero de 2014 a las 16:58

  • @ Evo510: Estoy confundido. Para usar el enmascaramiento, también debe conocer el relleno, que no está garantizado por el idioma. Entonces, no puedes usar máscaras. ¿Me estoy perdiendo de algo?

    – nes1983

    3 de enero de 2014 a las 19:58

  • @ nes1983 He visto un código de integración numérica que asume que todas sus entradas son flotantes en orden secuencial. Le pasas el puntero al primer valor a integrar, y al último, y escanea entre ellos. Sin embargo, mantiene la información en una estructura porque, para todo, excepto para la integración, es un formato más conveniente.

    – Cort Amón

    15 de abril de 2015 a las 6:03

  • Si bien violará el estándar, existe un método de reordenación útil para proteger el kernel de Linux de rootkits/exploits: parte de Linux KSPP (kernsec.org/wiki/index.php/Kernel_Self_Protection_Project) es la aleatorización/reordenación de algunos campos de estructura: openwall.com/lists/kernel-hardening/2017/05/26/8 (Introduzca el complemento de aleatorización del diseño de estructuras), documento relacionado: sec.taylor.edu/doc/… (“Seguridad del kernel mejorada a través de la aleatorización del diseño de la memoria” – DM Stanley – ‎2013)

    – osgx

    5 de junio de 2017 a las 15:47

Las estructuras se utilizan con frecuencia como representaciones del orden de empaquetado de formatos de archivos binarios y protocolos de red. Esto se rompería si se hiciera eso. Además, diferentes compiladores optimizarían las cosas de manera diferente y vincular el código de ambos sería imposible. Esto simplemente no es factible.

  • esto no tiene nada que ver con redes o estructuras de archivos. De hecho, el encabezado de una estructura BMP ESTÁ repleto de elementos que caen en límites no naturales que son ajenos al compilador.

    – Andrew Subvención

    22 de septiembre de 2008 a las 23:02

  • Err, ¿sí? Has malinterpretado la pregunta. Vuelva a leer el segundo párrafo, donde habla sobre el orden de las estructuras. Esto es completamente diferente del relleno.

    – Serafina Brocious

    22 de septiembre de 2008 a las 23:03

  • Tu primer punto es muy válido. pero creo que tu segundo no lo es. El código compilado de diferentes compiladores no es compatible de todos modos.

    – Johannes Schaub – litb

    18 de febrero de 2009 a las 19:29

  • @JohannesSchaub-litb eso depende; si ambos compiladores se adhieren a la misma ABI, no hay razón para que produzcan código incompatible. Algunos ejemplos son GCC y Clang, y GCC y MSVC de 32 bits para C en Windows.

    – rubenvb

    23 de mayo de 2013 a las 7:57

GCC es más inteligente que la mayoría de nosotros en la producción de código de máquina a partir de nuestro código fuente; sin embargo, me estremezco si fuera más inteligente que nosotros al reorganizar nuestras estructuras, ya que son datos que, por ejemplo, pueden escribirse en un archivo. Una estructura que comienza con 4 caracteres y luego tiene un entero de 4 bytes sería inútil si se lee en otro sistema donde GCC decidió que debería reorganizar los miembros de la estructura.

  • La lectura/escritura de estructuras directamente en un archivo no es un compilador/plataforma portátil de todos modos debido a la alineación (que está permitida), consulte esta respuesta SO.

    – formulador

    30 de enero de 2018 a las 19:57

gcc SVN tiene una optimización de reorganización de estructura (-fipa-struct-reorg), pero requiere un análisis de todo el programa y no es muy potente en este momento.

Los compiladores de C no empaquetan estructuras automáticamente precisamente porque de problemas de alineación como los que mencionas. Los accesos que no están en los límites de las palabras (32 bits en la mayoría de las CPU) conllevan una fuerte penalización en x86 y provocan trampas fatales en las arquitecturas RISC.

  • No estaba hablando de deshacerme del almacenamiento en búfer, estoy hablando de poner todos los largos/indicadores de extremo a extremo, luego todos los cortos de extremo a extremo, luego todos los caracteres de extremo a extremo, etc. para que solo pierdas espacio al final.

    – Alex Gartrell

    22 de septiembre de 2008 a las 22:53

  • Bueno, eso es verdad a medias. El compilador de C los empaquetará de manera predeterminada, solo lo hacen alineados con los límites de palabras naturales de la arquitectura. Es por eso que necesita #pragma pack(0) estructuras que usan caracteres/cortos en protocolos empaquetados, para evitar que agregue relleno.

    – Serafina Brocious

    22 de septiembre de 2008 a las 22:54

  • @Alex, error. Vas a desperdiciar la misma cantidad de espacio, ya que tu personaje tendría que tener la misma cantidad de relleno. No se beneficiaría en absoluto, ni en términos de espacio ni de rendimiento.

    – Serafina Brocious

    22 de septiembre de 2008 a las 22:55

  • Oh. Sí, eso causa problemas con los formatos binarios, como atestiguó Cody. Además, ANSI garantiza que las compensaciones de los elementos de la estructura deben estar en orden creciente.

    – Alex M.

    22 de septiembre de 2008 a las 22:57

  • no pierde ninguno de los beneficios del relleno al organizar la estructura correctamente. Con un carácter corto, char, puede tener un relleno de 0, pero todos los elementos caen en el desplazamiento correcto. En general, no perderá nada de velocidad por esto, ya que caen en sus límites naturales.

    – Alex Gartrell

    23 de septiembre de 2008 a las 0:53

avatar de usuario
michel

No digo que sea una buena idea, pero ciertamente puede escribir código que se base en el orden de los miembros de una estructura. Por ejemplo, como un truco, a menudo las personas lanzan un puntero a una estructura como el tipo de un determinado campo al que quieren acceder, luego usan la aritmética de punteros para llegar allí. Para mí, esta es una idea bastante peligrosa, pero la he visto usar, especialmente en C ++ para forzar que una variable que se ha declarado privada sea de acceso público cuando está en una clase de una biblioteca de terceros y no está encapsulada públicamente. Reordenar los miembros rompería eso por completo.

  • No estaba hablando de deshacerme del almacenamiento en búfer, estoy hablando de poner todos los largos/indicadores de extremo a extremo, luego todos los cortos de extremo a extremo, luego todos los caracteres de extremo a extremo, etc. para que solo pierdas espacio al final.

    – Alex Gartrell

    22 de septiembre de 2008 a las 22:53

  • Bueno, eso es verdad a medias. El compilador de C los empaquetará de manera predeterminada, solo lo hacen alineados con los límites de palabras naturales de la arquitectura. Es por eso que necesita #pragma pack(0) estructuras que usan caracteres/cortos en protocolos empaquetados, para evitar que agregue relleno.

    – Serafina Brocious

    22 de septiembre de 2008 a las 22:54

  • @Alex, error. Vas a desperdiciar la misma cantidad de espacio, ya que tu personaje tendría que tener la misma cantidad de relleno. No se beneficiaría en absoluto, ni en términos de espacio ni de rendimiento.

    – Serafina Brocious

    22 de septiembre de 2008 a las 22:55

  • Oh. Sí, eso causa problemas con los formatos binarios, como atestiguó Cody. Además, ANSI garantiza que las compensaciones de los elementos de la estructura deben estar en orden creciente.

    – Alex M.

    22 de septiembre de 2008 a las 22:57

  • no pierde ninguno de los beneficios del relleno al organizar la estructura correctamente. Con un carácter corto, char, puede tener un relleno de 0, pero todos los elementos caen en el desplazamiento correcto. En general, no perderá nada de velocidad por esto, ya que caen en sus límites naturales.

    – Alex Gartrell

    23 de septiembre de 2008 a las 0:53

avatar de usuario
Alaska

Es posible que desee probar la última versión de gcc trunk o struct-reorg-branch, que se encuentra en desarrollo activo.

https://gcc.gnu.org/wiki/cauldron2015?action=AttachFile&do=view&target=Olga+Golovanevsky_+Memory+Layout+Optimizations+of+Structures+and+Objects.pdf

¿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