81

質問のタイトルは少し奇妙かもしれませんが、私が知る限り、末尾呼び出しの最適化に反対するものは何もありません。ただし、オープンソースプロジェクトを閲覧しているときに、コンパイラが末尾呼び出しの最適化を実行するのを積極的に阻止しようとするいくつかの関数にすでに遭遇しました。たとえば、そのようなハックでいっぱいのCFRunLoopRefの実装です。例えば:

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
}

なぜこれがとても重要に見えるのか知りたいのですが、通常の開発者として私もこれを念頭に置いておく必要がある場合はありますか?例えば。末尾呼び出しの最適化に関する一般的な落とし穴はありますか?

4

3 に答える 3

83

ここでの私の推測は、それが__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__デバッグ目的でスタックトレースにあることを確認することです。それは__attribute__((no inline))この考えを裏付けるものを持っています。

お気づきの方もいらっしゃると思いますが、その関数はとにかく別の関数に移動してバウンスするので、デバッグを支援するためにそのような冗長な名前が付いているとしか思えないトランポリンの形式です。これは、関数が他の場所から登録された関数ポインターを呼び出しているため、その関数にデバッグシンボルがアクセスできない可能性がある場合に特に役立ちます。

同様のことを行う他の同様の名前の関数にも注意してください。バックトレースから何が起こったかを確認するのに役立つように見えます。これはコアMacOSXコードであり、クラッシュレポートとプロセスサンプルレポートにも表示されることに注意してください。

于 2012-05-28T22:03:43.343 に答える
34

これは単なる推測ですが、スタックオーバーフローエラーで爆破するのではなく、無限ループを回避するためかもしれません。

問題のメソッドはスタックに何も配置しないため、末尾呼び出しの再帰最適化では、リターンアドレスをスタックに配置する最適化されていないコードとは対照的に、無限ループに入るコードを生成できるように思われます。誤用した場合、最終的にはオーバーフローします。

私が持っている他の唯一の考えは、デバッグとスタックトレース印刷のためにスタック上の呼び出しを保持することに関するものです。

于 2012-05-28T21:56:30.407 に答える
21

考えられる理由の1つは、デバッグとプロファイリングを容易にすることです(TCOを使用すると、親スタックフレームが消え、スタックトレースを理解しにくくなります)。

于 2012-05-28T21:59:49.013 に答える