Le pedí a Google que me diera el significado de la gcc
opción -fomit-frame-pointer
, que me redirige a la siguiente declaración.
-fomit-frame-pointer
No guarde el puntero del marco en un registro para funciones que no lo necesitan. Esto evita las instrucciones para guardar, configurar y restaurar punteros de marco; también hace que un registro adicional esté disponible en muchas funciones. También hace que la depuración sea imposible en algunas máquinas.
Según mi conocimiento de cada función, se creará un registro de activación en la pila de la memoria del proceso para mantener todas las variables locales y algo más de información. Espero que este puntero de marco signifique la dirección del registro de activación de una función.
En este caso, ¿cuáles son el tipo de funciones para las que no es necesario mantener el puntero del marco en un registro? Si obtengo esta información, intentaré diseñar la nueva función en base a eso (si es posible) porque si el puntero del marco no se mantiene en los registros, algunas instrucciones se omitirán en binario. Esto realmente mejorará notablemente el rendimiento en una aplicación donde hay muchas funciones.
fuente
Release
y enDebug
realidad es muy útil, tome esta opción como ejemplo.Release
memoria de un cliente que ejecuta tu compilación.Respuestas:
La mayoría de las funciones más pequeñas no necesitan un puntero de marco; las funciones más grandes PUEDEN necesitarlo.
Realmente se trata de qué tan bien se las arregla el compilador para rastrear cómo se usa la pila y dónde están las cosas en la pila (variables locales, argumentos pasados a la función actual y argumentos en preparación para una función a punto de ser llamada). No creo que sea fácil caracterizar las funciones que necesitan o no necesitan un puntero de marco (técnicamente, NINGUNA función TIENE que tener un puntero de marco; es más un caso de "si el compilador considera necesario reducir la complejidad de otro código ").
No creo que debas "intentar hacer que las funciones no tengan un puntero de marco" como parte de tu estrategia de codificación; como dije, las funciones simples no las necesitan, así que úsalas
-fomit-frame-pointer
y obtendrás un registro más disponible. para el asignador de registros y guarde 1-3 instrucciones sobre la entrada / salida a las funciones. Si su función necesita un puntero de marco, es porque el compilador decide que es una mejor opción que no usar un puntero de marco. No es un objetivo tener funciones sin un puntero de marco, es un objetivo tener un código que funcione correctamente y rápidamente.Tenga en cuenta que "no tener un puntero de marco" debería ofrecer un mejor rendimiento, pero no es una fórmula mágica que ofrezca enormes mejoras, especialmente en x86-64, que ya tiene 16 registros para empezar. En x86 de 32 bits, dado que solo tiene 8 registros, uno de los cuales es el puntero de pila, y tomar otro como puntero de trama significa que se toma el 25% del espacio de registro. Cambiar eso al 12,5% es una gran mejora. Por supuesto, compilar para 64 bits también será de gran ayuda.
fuente
alloca
que mueve el puntero de la pila en una cantidad variable. La omisión del puntero de fotograma dificulta significativamente la depuración. Las variables locales son más difíciles de localizar y las trazas de la pila son mucho más difíciles de reconstruir sin un puntero de marco para ayudar. Además, acceder a los parámetros puede resultar más caro, ya que están lejos de la parte superior de la pila y pueden requerir modos de direccionamiento más costosos.alloca
[¿quién lo hace? - Estoy 99% seguro de que nunca he escrito código que usealloca
] ovariable size local arrays
[que es una forma moderna dealloca
], entonces el compilador PUEDE decidir que usar frame-pointer es una mejor opción, porque los compiladores están escritos para no seguir ciegamente el opciones dadas, pero le dan las mejores opciones.alloca
: se desechan tan pronto como abandona el ámbito en el que están declarados, mientras que elalloca
espacio solo se libera cuando abandona la función. Esto hace que VLA sea mucho más fácil de seguir quealloca
, creo.-fomit-frame-pointer
de forma predeterminada para x86-64.alloca
'espacio ed) es desconocido en el momento de la compilación . Por lo general, el compilador usará el puntero del marco para obtener la dirección de las variables locales, si el tamaño del marco de la pila no cambia, puede ubicarlas en un desplazamiento fijo del puntero de la pila.Se trata del registro BP / EBP / RBP en plataformas Intel. Este registro tiene como valor predeterminado el segmento de pila (no necesita un prefijo especial para acceder al segmento de pila).
(fuente: http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm )
Dado que en la mayoría de las plataformas de 32 bits, el segmento de datos y el segmento de la pila son iguales, esta asociación de EBP / RBP con la pila ya no es un problema. Lo mismo ocurre con las plataformas de 64 bits: la arquitectura x86-64, introducida por AMD en 2003, ha eliminado en gran medida el soporte para la segmentación en el modo de 64 bits: cuatro de los registros de segmento: CS, SS, DS y ES están forzados a 0 Estas circunstancias de las plataformas x86 de 32 y 64 bits significan esencialmente que el registro EBP / RBP se puede utilizar, sin ningún prefijo, en las instrucciones del procesador que acceden a la memoria.
Entonces, la opción del compilador sobre la que escribiste permite que BP / EBP / RBP se use para otros medios, por ejemplo, para mantener una variable local.
Por "Esto evita las instrucciones para guardar, configurar y restaurar punteros de cuadro" se entiende evitar el siguiente código en la entrada de cada función:
o la
enter
instrucción, que fue muy útil en los procesadores Intel 80286 y 80386.Además, antes del retorno de la función, se usa el siguiente código:
o la
leave
instrucción.Las herramientas de depuración pueden escanear los datos de la pila y utilizar estos datos de registro EBP insertados mientras se localizan
call sites
, es decir, para mostrar los nombres de la función y los argumentos en el orden en que se han llamado jerárquicamente.Los programadores pueden tener preguntas sobre los marcos de pila no en un término amplio (que es una entidad única en la pila que sirve solo una llamada de función y mantiene la dirección de retorno, argumentos y variables locales) pero en un sentido estricto - cuando el término
stack frames
se menciona en el contexto de las opciones del compilador. Desde la perspectiva del compilador, un marco de pila es solo el código de entrada y salida de la rutina , que empuja un ancla a la pila, que también se puede usar para depurar y manejar excepciones. Las herramientas de depuración pueden escanear los datos de la pila y utilizar estos anclajes para realizar un seguimiento, mientras se ubicancall sites
en la pila, es decir, para mostrar los nombres de la función en el orden en que se han llamado jerárquicamente.Por eso es muy importante que un programador comprenda qué es un marco de pila en términos de opciones del compilador, porque el compilador puede controlar si generar este código o no.
En algunos casos, el compilador puede omitir el marco de la pila (código de entrada y salida para la rutina) y se accederá directamente a las variables a través del puntero de la pila (SP / ESP / RSP) en lugar del puntero base conveniente (BP / ESP / RSP). Las condiciones para que un compilador omita los marcos de pila para algunas funciones pueden ser diferentes, por ejemplo: (1) la función es una función hoja (es decir, una entidad final que no llama a otras funciones); (2) no se utilizan excepciones; (3) no se llaman rutinas con parámetros salientes en la pila; (4) la función no tiene parámetros.
Omitir marcos de pila (código de entrada y salida para la rutina) puede hacer que el código sea más pequeño y más rápido, pero también puede afectar negativamente la capacidad de los depuradores para rastrear los datos en la pila y mostrarlos al programador. Estas son las opciones del compilador que determinan bajo qué condiciones debe satisfacer una función para que el compilador le otorgue el código de entrada y salida del marco de pila. Por ejemplo, un compilador puede tener opciones para agregar dicho código de entrada y salida a las funciones en los siguientes casos: (a) siempre, (b) nunca, (c) cuando sea necesario (especificando las condiciones).
Volviendo de las generalidades a las particularidades: si usa la
-fomit-frame-pointer
opción del compilador GCC, puede ganar tanto en el código de entrada como en el de salida de la rutina, y en tener un registro adicional (a menos que ya esté activado de forma predeterminada, ya sea por sí mismo o implícitamente por otros opciones, en este caso, ya se está beneficiando de la ganancia de usar el registro EBP / RBP y no se obtendrá ninguna ganancia adicional especificando explícitamente esta opción si ya está activada implícitamente). Sin embargo, tenga en cuenta que en los modos de 16 y 32 bits, el registro BP no tiene la capacidad de acceder a partes de 8 bits como lo ha hecho AX (AL y AH).Dado que esta opción, además de permitir que el compilador use EBP como un registro de propósito general en las optimizaciones, también evita la generación de códigos de entrada y salida para el marco de la pila, lo que complica la depuración, es por eso que la documentación de GCC establece explícitamente (inusualmente enfatizado con negrita estilo) que habilitar esta opción hace que la depuración sea imposible en algunas máquinas
También tenga en cuenta que otras opciones del compilador, relacionadas con la depuración u optimización, pueden activar
-fomit-frame-pointer
o desactivar implícitamente la opción.No encontré ninguna información oficial en gcc.gnu.org sobre cómo afectan otras opciones
-fomit-frame-pointer
en las plataformas x86 , https://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Optimize-Options.html solo establece lo siguiente:Por lo tanto, no está claro de la documentación en sí si
-fomit-frame-pointer
se activará si solo compila con una sola-O
opción en la plataforma x86. Puede probarse empíricamente, pero en este caso, los desarrolladores de GCC no se comprometen a no cambiar el comportamiento de esta opción en el futuro sin previo aviso.Sin embargo, Peter Cordes ha señalado en los comentarios que existe una diferencia en la configuración predeterminada
-fomit-frame-pointer
entre las plataformas x86-16 y las plataformas x86-32 / 64.Esta opción -
-fomit-frame-pointer
- también es relevante para Intel C ++ Compiler 15.0 , no solo para GCC:Para el compilador Intel, esta opción tiene un alias
/Oy
.Esto es lo que Intel escribió al respecto:
Tenga en cuenta que la cita anterior solo es relevante para el compilador Intel C ++ 15, no para GCC.
fuente
gcc -m16
existe, pero ese es un caso especial extraño que básicamente hace código de 32 bits que se ejecuta en modo de 16 bits usando prefijos por todas partes. También tenga en cuenta que-fomit-frame-pointer
se ha habilitado de forma predeterminada durante años en x86-m32
, y más tiempo que en x86-64 (-m64
).