El título de la pregunta puede ser un poco extraño, pero la cuestión es que, hasta donde yo sé, no hay nada que hable en absoluto en contra de la optimización de llamadas finales. Sin embargo, mientras navegaba por proyectos de código abierto, ya me encontré con algunas funciones que intentan activamente evitar que el compilador realice una optimización de llamadas finales , por ejemplo, la implementación de CFRunLoopRef, que está llena de tales trucos . Por ejemplo:
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
if (func) {
func(observer, activity, info);
}
getpid(); // thwart tail-call optimization
}
Me encantaría saber por qué esto es aparentemente tan importante, y ¿hay algún caso en el que yo, como desarrollador normal, deba tener esto en cuenta también? P.ej. ¿Existen errores comunes con la optimización de llamadas de cola?
getpid()
no se está utilizando, ¿no podría ser eliminado por un optimizador informado (ya quegetpid
es una función que se sabe que no tiene efectos secundarios), permitiendo por lo tanto al compilador hacer una optimización de llamada final de todos modos? Este parece un mecanismo realmente frágil.Respuestas:
Mi suposición aquí es que es para asegurar que
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
esté en el seguimiento de la pila para fines de depuración. Tiene lo__attribute__((no inline))
que respalda esta idea.Si se da cuenta, esa función simplemente va y rebota a otra función de todos modos, por lo que es una forma de trampolín que solo puedo pensar que está ahí con un nombre tan detallado para ayudar a la depuración. Esto sería especialmente útil dado que la función está llamando a un puntero de función que se ha registrado desde otro lugar y, por lo tanto, es posible que esa función no tenga símbolos de depuración accesibles.
Observe también las otras funciones con nombres similares que hacen cosas similares; realmente parece que está ahí para ayudar a ver lo que ha sucedido a partir de un rastreo. Tenga en cuenta que este es el código principal de Mac OS X y también aparecerá en los informes de fallos y en los informes de muestra de procesos.
fuente
__attribute__((noinline))
. Creo que acertaste aquí.__CFRunLoopDoObservers
que definitivamente aparece en el seguimiento de la pila ...Esto es solo una suposición, pero tal vez para evitar un bucle infinito frente a un bombardeo con un error de desbordamiento de pila.
Dado que el método en cuestión no pone nada en la pila, parecería posible que la optimización de la recursividad de la llamada de cola produzca código que ingrese en un bucle infinito en lugar del código no optimizado que pondría la dirección de retorno en la pila que eventualmente se desbordaría en caso de mal uso.
El único otro pensamiento que tengo está relacionado con la conservación de las llamadas en la pila para la depuración y la impresión de seguimiento de la pila.
fuente
Una posible razón es facilitar la depuración y la creación de perfiles (con el TCO, el marco de la pila principal desaparece, lo que dificulta la comprensión de los seguimientos de la pila).
fuente