c optimization compiler-construction

¿Cuándo debo omitir el puntero del cuadro?



optimization compiler-construction (4)

A menudo, puede obtener un código de ensamblaje más significativo de GCC utilizando el argumento -S para generar el ensamblaje:

$ gcc code.c -S -o withfp.s $ gcc code.c -S -o withoutfp.s -fomit-frame-pointer $ diff -u withfp.s withoutfp.s

A GCC no le importa la dirección, por lo que podemos comparar las instrucciones reales generadas directamente. Para la función de la hoja, esto da:

myf: - pushl %ebp - movl %esp, %ebp - movl 12(%ebp), %eax - addl 8(%ebp), %eax - popl %ebp + movl 8(%esp), %eax + addl 4(%esp), %eax ret

GCC no genera el código para insertar el puntero de marco en la pila, y esto cambia la dirección relativa de los argumentos pasados ​​a la función en la pila.

¿Hay alguna optimización sustancial al omitir el puntero de marco? Si he entendido correctamente leyendo this página, se -fomit-frame-pointer cuando queremos evitar guardar, configurar y restaurar los punteros de cuadro.

¿Se hace esto solo para cada llamada de función y, si es así, vale la pena evitar algunas instrucciones para cada función? ¿No es trivial para una optimización? ¿Cuáles son las implicaciones reales de usar esta opción aparte de las limitaciones de depuración?

Compilé el siguiente código C con y sin esta opción

int main(void) { int i; i = myf(1, 2); } int myf(int a, int b) { return a + b; }

,

# gcc -S -fomit-frame-pointer code.c -o withoutfp.s # gcc -S code.c -o withfp.s

.

diff -u de los dos archivos, se reveló el siguiente código de ensamblaje:

--- withfp.s 2009-12-22 00:03:59.000000000 +0000 +++ withoutfp.s 2009-12-22 00:04:17.000000000 +0000 @@ -7,17 +7,14 @@ leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) - pushl %ebp - movl %esp, %ebp pushl %ecx - subl $36, %esp + subl $24, %esp movl $2, 4(%esp) movl $1, (%esp) call myf - movl %eax, -8(%ebp) - addl $36, %esp + movl %eax, 20(%esp) + addl $24, %esp popl %ecx - popl %ebp leal -4(%ecx), %esp ret .size main, .-main @@ -25,11 +22,8 @@ .globl myf .type myf, @function myf: - pushl %ebp - movl %esp, %ebp - movl 12(%ebp), %eax - addl 8(%ebp), %eax - popl %ebp + movl 8(%esp), %eax + addl 4(%esp), %eax ret .size myf, .-myf .ident "GCC: (GNU) 4.2.1 20070719

¿Podría alguien aclarar los puntos clave del código anterior donde -fomit-frame-point realmente marcó la diferencia?

Edición: la salida de objdump reemplazada con gcc -S ''s


El único inconveniente de omitirlo es que la depuración es mucho más difícil.

La ventaja principal es que hay un registro de propósito general adicional que puede hacer una gran diferencia en el rendimiento. Obviamente, este registro adicional se usa solo cuando es necesario (probablemente en su función muy simple no lo es); En algunas funciones hace más diferencia que en otras.


Perfile su programa para ver si hay una diferencia significativa.

A continuación, perfile su proceso de desarrollo. ¿La depuración es más fácil o más difícil? ¿Pasas más tiempo desarrollando o menos?

Las optimizaciones sin perfilar son una pérdida de tiempo y dinero.


-fomit-frame-pointer permite que un registro adicional esté disponible para uso general. Supongo que esto es realmente solo un gran problema en x86 de 32 bits, que está un poco hambriento de registros. *

Se podría esperar que EBP ya no se guarde ni se ajuste en cada llamada a la función, y probablemente un uso adicional de EBP en el código normal, y menos operaciones de apilamiento en ocasiones en las que EBP se use como un registro de propósito general.

Su código es demasiado simple para ver cualquier beneficio de este tipo de optimización: no está utilizando suficientes registros. Además, no ha activado el optimizador, lo que puede ser necesario para ver algunos de estos efectos.

* Registros ISA, no registros micro-arquitectura.