为什么代码会主动尝试阻止尾调优化?

Jus*_*Sid 79 c c++ optimization compiler-optimization tail-call-optimization

这个问题的标题可能有点奇怪,但事实是,据我所知,根本没有什么可以反对尾部调用优化.但是,在浏览开源项目时,我已经遇到了一些主动尝试阻止编译器进行尾调用优化的函数,例如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
}
Run Code Online (Sandbox Code Playgroud)

我很想知道为什么这看起来如此重要,有没有我作为一个普通的开发人员应该保持这种想法呢?例如.尾调用优化有常见的陷阱吗?

mat*_*way 82

我的猜测是,确保__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__在堆栈跟踪中进行调试.它__attribute__((no inline))支持这个想法.

如果你注意到,那个函数只是去反弹到另一个函数,所以它是一种蹦床形式,我只能想到它有一个冗长的名称来帮助调试.这将是特别有用的,因为该函数正在调用已从其他地方注册的函数指针,因此该函数可能没有可访问的调试符号.

还要注意其他类似命名的函数,这些函数执行类似的操作 - 它看起来确实有助于查看从回溯中发生的事情.请记住,这是核心Mac OS X代码,也会出现在崩溃报告和流程样本报告中.

  • 我认为这是最好的答案.+1 (2认同)

And*_*ite 33

这只是一个猜测,但可能是为了避免无限循环与使用堆栈溢出错误进行轰炸.

由于所讨论的方法没有在堆栈上放置任何内容,因此尾调用递归优化可能会产生进入无限循环的代码,而不是将返回地址放在堆栈上的非优化代码在滥用的情况下最终会溢出.

我唯一的另一个想法是保留堆栈上的调用以进行调试和堆栈跟踪打印.

  • 我认为堆栈跟踪/调试解释更有可能(我即将发布它).无限循环并不比崩溃更糟糕,因为用户可以强制应用程序退出.这也可以解释一下这个问题. (8认同)
  • @ughoavgfhw:也许,但是当你进入线程时,无限循环真的很难追查.我一直都认为滥用会引发异常.因为我从来没有这么做过,所以仍然只是猜测. (3认同)
  • @AndrewWhite嗯我完全喜欢无限循环 - 我想不出一个更容易调试的东西,我的意思是你可以只需附加调试器并获得问题的确切位置和状态而无需任何猜测.但是如果你想从用户那里获得堆栈跟踪,我同意无限循环是有问题的,所以这似乎是合乎逻辑的 - 一个错误将出现在你的日志中,一个无限循环不会. (2认同)

NPE*_*NPE 21

一个潜在的原因是使调试和分析更容易(使用TCO,父堆栈框架消失,这使得堆栈跟踪更难理解.)

  • 以减慢程序为代价使分析更容易,但有点奇怪.在测量汽车的行驶距离之前,它可以像稀释油一样有意义:x (2认同)