コードが末尾呼び出しの最適化を積極的に防止しようとするのはなぜですか?


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
}

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


10
考えられる落とし穴の1つは、アプリケーションが複数のプラットフォームでスムーズに動作し、末尾呼び出しの最適化をサポートしていないコンパイラでコンパイルすると突然動作を停止することです。この最適化は、実際にはパフォーマンスを向上させるだけでなく、ランタイムエラー(スタックオーバーフロー)を防ぐことができることを忘れないでください。
Niklas B.

5
@NiklasB。しかし、これはそれを無効にしようとしない理由ではありませんか?
JustSid 2012年

4
システムコールはTCOを回避する確実な方法かもしれませんが、かなり高価な方法でもあります。
フレッドフー2012年

39
これは、適切なコメントをするための素晴らしい教えられる瞬間です。(末尾呼び出しの最適化を防ぐために)その行が存在する理由を部分的に説明する場合は+1、最初に末尾呼び出しの最適化を無効にする必要がある理由を説明しない場合は-100 ...
Mark Sowul 2012年

16
の値はgetpid()使用されていないので、情報に基づいたオプティマイザーによって削除できませんでした(getpid副作用がないことがわかっている関数であるため)。したがって、コンパイラーはとにかく末尾呼び出しの最適化を実行できますか?これは本当に壊れやすいメカニズムのようです。
luiscubal 2012年

回答:


83

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

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

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


はい、それはと一致してい__attribute__((noinline))ます。私はあなたがここにいると思います。
Niklas B.

はい、確かに理にかなっています。これらの関数が呼び出される場合、あなたが見ている場合でも、あなたは私の例の機能のみから呼び出され、たとえば、彼らは常に一つだけの関数から呼び出されていることがわかります__CFRunLoopDoObservers...間違いなく、スタックトレースに表示された
JustSid

1
もちろんですが、オブザーバーのコールバック/ブロックなどが実行されている場所を正確に示すもう1つのマーカーだと思います。
mattjgalloway 2012年

2
これがベストアンサーだと思います。+1
R .. GitHub STOP HELPING ICE 2012

@R ..しかし、私は1つの答えしか受け入れることができず、Andrew Whiteは、末尾呼び出しの最適化が望まれない可能性がある他のケースにも名前を付けました。覚えておいてください、私はなぜ関数がそれをしたのか尋ねませんでしたが、なぜそれが一般的に望まれないのか、そして実際の例として関数を与えました。
JustSid 2012年

34

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

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

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


8
スタックトレース/デバッグの説明の可能性がはるかに高いと思います(そして私はそれを投稿しようとしていました)。ユーザーはアプリケーションを強制的に終了できるため、無限ループはクラッシュよりも悪くはありません。それはまたnoinlineを説明するでしょう。
ughoavgfhw 2012年

3
@ughoavgfhw:たぶん、でもスレッド化などに入ると、無限ループを追跡するのは本当に難しいです。私はいつも、誤用は例外を引き起こすはずだという考え方を持っていました。私はこれをする必要がなかったので、それはまだ単なる推測です。
アンドリューホワイト

1
シンクロニシティ、ある種...アプリケーションが新しいウィンドウを開いたままにするという悪いバグに遭遇しました。これにより、「ヒープ」(私のメモリ)を飽和させてXを窒息させる前にアプリケーションがクラッシュした場合、クレイジーなアプリを突然強制終了するためにターミナルに切り替える必要はなかったと思います(Xはすぐに応答しない)。したがって、おそらく、スタックオーバーフローが発生し、最適化が行われない可能性のある「フェイルファスト」アプローチを好む理由になるでしょう...?または多分それはただ別の問題ですが...!
新武蔵2012年

2
@AndrewWhiteうーん私は無限ループが大好きです-デバッグが簡単なことは1つも考えられません。つまり、デバッガーを接続するだけで、推測せずに問題の正確な位置と状態を取得できます。しかし、ユーザーからスタックトレースを取得したい場合は、無限ループに問題があることに同意します。これは論理的に思えます。ログにエラーが表示されますが、無限ループは表示されません。
Voo 2012年

1
これは、そもそも関数が再帰的であることを前提としていますが、そうではありません。直接的にも(関数が由来するコンテキストを見て)間接的にも。私は最初に同じ誤った仮定をしました。
コンラッドルドルフ2012年

21

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


2
プログラムの速度を落とすことを犠牲にしてプロファイリングを簡単にするのはちょっと奇妙です。車がどこまで行けるかを測定する前にオイルを希釈するのと同じくらい理にかなっています:x
Matthieu M. 2012

1
@MatthieuM。:追加された呼び出しがループ内で数百万回実行された場合、そのようなことは意味がありませんが、1秒間に数百回以下実行された場合は、実際のシステムに残しておく方がよい場合があります。実際のシステムを取り出すよりも、実際のシステムがどのように動作するかを調べることができ、そのような削除によってシステムの動作に微妙ではあるが重要な変化が生じるリスクがあります。
スーパーキャット2015

@MatthieuM。オイルを希釈することが測定の前提条件である場合、それは実際には完全に理にかなっています。
Dmitry Grigoryev 2016年

@DmitryGrigoryev:いいえ、そうではありません。煩わしい対策はありませんが、間違った対策は役に立たないものから危険なものまであります(信頼の度合いによって異なります)。オイルの例えを続けると、速度が低下する場合は、重量が空気力学よりも重要であることを示す測定値が得られる可能性があります。したがって、重量を取り除き、空気力学を悪化させて、測定したものに最適化します...ただし、実際のオイルでは、速く進むと、空気力学がより重要であり、何もしないよりも「改善」が悪いことがわかります。
Matthieu M.

@MatthieuM。不確定性原理に精通していますか?測定対象物と相互作用せずに何かを測定する方法がないため、測定はある程度間違っています。したがって、例でオイルを変更しなくても、車を計測すると、とにかく空気力学が変化します。
Dmitry Grigoryev 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.