Esto es algo que siempre he pensado que es cierto, pero nunca he tenido ninguna validación. Considere una función muy simple:
int subtractFive(int num) { return num -5; }Si una llamada a esta función usa una constante de tiempo de compilación como
getElement(5);Es muy probable que un compilador con optimizaciones activadas alinee esto. Sin embargo, lo que no me queda claro es si el num - 5 se evaluará en tiempo de ejecución o en tiempo de compilación. ¿Se extenderá la simplificación de expresiones recursivamente a través de funciones en línea de esta manera? ¿O no trasciende funciones?
Simplemente podemos mirar el ensamblaje generado para averiguarlo. Este código:
int subtractFive(int num) { return num -5; } int main(int argc, char *argv[]) { return subtractFive(argc); } compilado con rendimientos de g++ -O2
leal -5(%rdi), %eax retEntonces, la llamada a la función se redujo de hecho a una sola instrucción. Esta técnica de optimización se conoce como inline .
Por supuesto, uno puede usar la misma técnica para ver qué tan lejos llegará un compilador con eso, por ejemplo, el un poco más complicado
int subtractFive(int num) { return num -5; } int foo(int i) { return subtractFive(i) * 5; } int main(int argc, char *argv[]) { return foo(argc); }todavía se compila a
leal -25(%rdi,%rdi,4), %eax ret así que aquí ambas funciones fueron eliminadas en tiempo de compilación. Si la entrada a foo se conoce en el momento de la compilación, la llamada a la función (en este caso) simplemente se reemplazará por la constante resultante en el momento de la compilación ( Live ).
El compilador también puede combinar esta alineación con el plegado constante, para reemplazar la llamada a la función con su resultado completamente evaluado si todos los argumentos son constantes de tiempo de compilación. Por ejemplo,
int subtractFive(int num) { return num -5; } int foo(int i) { return subtractFive(i) * 5; } int main() { return foo(7); }compila a
mov eax, 10 retque es equivalente a
int main () { return 10; }Un compilador siempre hará esto cuando crea que es una buena idea, y (por lo general) es mucho mejor que usted para optimizar el código en este nivel bajo.
Es fácil hacer una pequeña prueba; considera lo siguiente
int foo(int); int bar(int x) { return x-5; } int baz() { return foo(bar(5)); } Al compilar con g++ -O3 , la salida de asm para la función baz es
xorl %edi, %edi jmp _Z3fooi Este código carga un 0 en el primer parámetro y luego salta al código de foo . Entonces, el código de la barra desapareció por completo y el cálculo del valor para pasar a foo se realizó en tiempo de compilación.
Además, devolver el valor de llamar a la función se convirtió en solo un salto al código de función (esto se llama "optimización de llamada de cola").
Un compilador inteligente evaluará esto en el momento de la compilación y reemplazará getElement(5) porque nunca tendrá un resultado diferente. Ninguna de las variables se considera volátil.