performance - ¿Cuál es el propósito del registro del puntero de marco EBP?
assembly x86 (5)
Sin embargo, x86 tiene muy pocos registros
Esto es cierto solo en el sentido de que los códigos de operación solo pueden abordar 8 registros. El procesador en sí tendrá muchos más registros que eso y utilizará el cambio de nombre de registro, el pipeline, la ejecución especulativa y otras palabras de moda del procesador para superar ese límite. Wikipedia tiene un buen párrafo introductorio sobre lo que un procesador x86 puede hacer para superar el límite de registro: http://en.wikipedia.org/wiki/X86#Current_implementations .
Soy un principiante en lenguaje ensamblador y he notado que el código x86 emitido por los compiladores generalmente mantiene el puntero del marco incluso en modo liberación / optimizado cuando podría usar el registro EBP
para otra cosa.
Entiendo por qué el puntero de marco podría hacer que el código sea más fácil de depurar, y podría ser necesario si se llama a alloca()
dentro de una función. Sin embargo, x86 tiene muy pocos registros y el uso de dos de ellos para mantener la ubicación del marco de la pila cuando uno sería suficiente simplemente no tiene sentido para mí. ¿Por qué se considera que omitir el puntero del marco es una mala idea incluso en construcciones optimizadas / de lanzamiento?
Depende del compilador, sin duda. He visto un código optimizado emitido por compiladores x86 que usa libremente el registro EBP como un registro de propósito general. (No recuerdo con qué compilador lo noté, sin embargo).
Los compiladores también pueden elegir mantener el registro EBP para ayudar con el desenrollado de la pila durante el manejo de excepciones, pero nuevamente esto depende de la implementación precisa del compilador.
El puntero de marco es un puntero de referencia que permite a un depurador saber dónde está la variable local o un argumento con un solo desplazamiento constante. Aunque el valor de ESP cambia durante el curso de la ejecución, EBP sigue siendo el mismo, permitiendo alcanzar la misma variable con el mismo desplazamiento (como el primer parámetro siempre estará en EBP + 8, mientras que los desplazamientos ESP pueden cambiar significativamente ya que estarás presionando / haciendo estallar cosas)
¿Por qué los compiladores no tiran el puntero del marco? Debido a que con el puntero de marco, el depurador puede averiguar dónde las variables locales y los argumentos están usando la tabla de símbolos, ya que se garantiza que estarán en una compensación constante para EBP. De lo contrario, no hay una manera fácil de determinar dónde está una variable local en cualquier punto del código.
Como Greg mencionó, también ayuda a desenrollar la pila para un depurador, ya que EBP proporciona una lista enlazada inversa de fotogramas de pila, lo que permite al depurador calcular el tamaño del fotograma de la pila (variables locales + argumentos) de la función.
La mayoría de los compiladores brindan la opción de omitir punteros de marco, aunque dificultan la depuración. Esa opción nunca se debe usar globalmente, ni siquiera en el código de lanzamiento. No sabe cuándo deberá depurar el bloqueo de un usuario.
Solo agregué mis dos centavos a las respuestas ya buenas.
Es parte de una buena arquitectura de lenguaje tener una cadena de marcos de pila. El BP apunta al cuadro actual, donde se almacenan las variables locales de subrutina. (Los locales están en compensaciones negativas, y los argumentos están en compensaciones positivas).
La idea de que está impidiendo que un registro perfectamente bueno sea utilizado en la optimización plantea la pregunta: ¿cuándo y dónde la optimización realmente vale la pena?
La optimización solo vale la pena en bucles apretados que 1) no llaman funciones, 2) donde el contador de programa gasta una fracción significativa de su tiempo, y 3) en código que el compilador realmente verá alguna vez (es decir, funciones que no son de biblioteca). Esta suele ser una fracción muy pequeña del código general, especialmente en sistemas grandes.
Otro código se puede retorcer y exprimir para deshacerse de los ciclos, y simplemente no importará, porque el contador del programa prácticamente nunca está allí.
Sé que no me lo preguntaste, pero en mi experiencia, el 99% de los problemas de rendimiento no tienen nada que ver con la optimización del compilador. Tienen todo que ver con el exceso de diseño.
Usar marcos de pila se ha vuelto increíblemente barato en cualquier hardware remotamente moderno. Si tiene marcos de pila baratos, guardar un par de registros no es tan importante. Estoy seguro de que las tramas rápidas de apilamiento contra más registros fueron un compromiso de ingeniería, y ganaron las tramas rápidas de apilamiento.
¿Cuánto estás ahorrando yendo puro registro? ¿Vale la pena?