Der Titel der Frage mag etwas seltsam sein, aber die Sache ist, dass meines Wissens nichts gegen die Optimierung von Tail Calls spricht. Beim Durchsuchen von Open Source-Projekten bin ich jedoch bereits auf einige Funktionen gestoßen, die aktiv versuchen, den Compiler daran zu hindern, eine Tail-Call-Optimierung durchzuführen, beispielsweise die Implementierung von CFRunLoopRef, das voll von solchen Hacks ist . Zum Beispiel:
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
}
Ich würde gerne wissen, warum dies scheinbar so wichtig ist, und gibt es Fälle, in denen ich als normaler Entwickler dies auch beachten sollte? Z.B. Gibt es häufige Fallstricke bei der Tail-Call-Optimierung?
getpid()
nicht verwendet wird, kann er nicht von einem informierten Optimierer entfernt werden (dagetpid
eine Funktion bekanntermaßen keine Nebenwirkungen hat), sodass der Compiler trotzdem eine Tail-Call-Optimierung durchführen kann? Dies scheint ein wirklich fragiler Mechanismus zu sein.Antworten:
Ich vermute hier, dass es sicherstellen soll, dass es sich zu
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
Debugging-Zwecken im Stack-Trace befindet. Es hat__attribute__((no inline))
was diese Idee stützt.Wenn Sie bemerken, dass diese Funktion ohnehin nur zu einer anderen Funktion wechselt, handelt es sich also um eine Form des Trampolins, von der ich nur glauben kann, dass sie einen so ausführlichen Namen enthält, um das Debuggen zu erleichtern. Dies wäre besonders hilfreich, da die Funktion einen Funktionszeiger aufruft, der von einer anderen Stelle registriert wurde, und daher auf diese Funktion möglicherweise keine Debugging-Symbole zugreifen können.
Beachten Sie auch die anderen ähnlich benannten Funktionen, die ähnliche Dinge tun - es sieht wirklich so aus, als ob sie dazu beitragen, zu sehen, was aus einer Rückverfolgung passiert ist. Beachten Sie, dass dies der Kerncode von Mac OS X ist und auch in Absturzberichten angezeigt und Beispielberichte verarbeitet werden.
quelle
__attribute__((noinline))
. Ich denke, Sie sind hier genau richtig.__CFRunLoopDoObservers
die definitiv in der StapelverfolgungDies ist nur eine Vermutung, aber vielleicht, um eine Endlosschleife zu vermeiden, anstatt mit einem Stapelüberlauffehler zu bombardieren.
Da die fragliche Methode nichts auf den Stapel legt, scheint es für die Tail-Call-Rekursionsoptimierung möglich zu sein, Code zu erzeugen, der in eine Endlosschleife eintritt, im Gegensatz zu dem nicht optimierten Code, der die Rücksprungadresse auf den Stapel legt die im Falle eines Missbrauchs schließlich überlaufen würde.
Der einzige andere Gedanke, den ich habe, bezieht sich auf das Beibehalten der Aufrufe auf dem Stapel zum Debuggen und Stacktrace-Drucken.
quelle
Ein möglicher Grund besteht darin, das Debuggen und Profilieren zu vereinfachen (mit TCO verschwindet der übergeordnete Stapelrahmen, wodurch es schwieriger wird, Stapelspuren zu verstehen.)
quelle