简体   繁体   English

在C ++中进行尾递归

[英]Doing tail recursion in C++

My function can be written much more simply if I do a tail call recursion (as opposed to a for (;;)...break loop). 如果我进行尾调用递归(而不是for (;;)...break循环),我的函数可以更简单地编写。 Yet I'm afraid I'll have performance problems if the compiler fails to optimize it, especially because it will be compiled by the end user. 但是,如果编译器无法优化它,我恐怕会遇到性能问题,特别是因为它将由最终用户编译。

  1. Is there a way to tell the compiler "Make sure you optimize this tail call, or else give me an error" (eg Scala supports this) 有没有办法告诉编译器“确保你优化这个尾调用,否则给我一个错误”(例如Scala支持这个)

  2. If the compiler can't optimize it away, what are the performance limits? 如果编译器无法优化它,那么性能限制是什么? About how many tail calls can I expect to be able to run without breaking the stack? 关于有多少尾调用可以在不打破堆栈的情况下运行?


UPDATE: 更新:

Compilers are gcc and MSVC. 编译器是gcc和MSVC。

Typically, I'll expect about a dozen tail calls. 通常,我会期待大约十几个尾调用。 But the extreme case could have thousands. 但极端情况下可能有数千人。 Platform is a typical low end laptop (eg Core i3 or i5). 平台是典型的低端笔记本电脑(例如Core i3或i5)。

No, there is no way to tell a compiler that tail recursion is required. 不,没有办法告诉编译器需要尾递归。 Some compilers (none that I'm aware of) may support implementation-specific annotations, but that requires the user to use that specific compiler. 一些编译器(我不知道)可能支持特定于实现的注释,但这需要用户使用该特定编译器。 Some other compilers, in some modes, intentionally never support tail calls, because they can provide a better debugging experience by not supporting tail calls. 在某些模式下,其他一些编译器故意不支持尾调用,因为它们可以通过不支持尾调用来提供更好的调试体验。 The user may be using such a compiler. 用户可能正在使用这样的编译器。

The allowed recursion depth is highly program-, function- and implementation-dependent, and no sensible numbers can be given for it. 允许的递归深度高度依赖于程序,功能和实现,并且不能给出合理的数字。 Given a specific platform, you can probably determine the default stack size, investigate the frame size for one particular compiler on that platform, and do a simple division to get a rough estimate of how many nested calls you can have. 给定一个特定的平台,您可以确定默认的堆栈大小,调查该平台上某个特定编译器的帧大小,并进行简单的划分以粗略估计您可以拥有多少嵌套调用。

I recommend rewriting it in a way that makes it clear to the reader what is happening, but does not rely on a compiler optimising tail calls. 我建议以一种方式重写它,使读者清楚地知道发生了什么,但不依赖编译器优化尾调用。 Although hated, the goto statement can be very useful for this. 虽然讨厌,但goto语句对此非常有用。

Take a simple tail-recursive bit-counting function: 采用简单的尾递归位计数功能:

int bitcount(unsigned int n, int acc = 0) {
  if (n == 0)
    return acc;

  return bitcount(n >> 1, acc + (n & 1));
}

It can be trivially rewritten as 它可以简单地重写为

int bitcount(unsigned int n, int acc = 0) {
tail_recurse:
  if (n == 0)
    return acc;

  // tail return bitcount(n >> 1, acc + (n & 1));
  acc += n & 1;
  n = n >> 1;
  goto tail_recurse;
}

Of course this is a simple version that is trivially rewritten to avoid recursion entirely, and probably shouldn't even be implemented manually, but the specific transformation I've used here is one that you can apply to any function where tail recursion is possible and where you need tail recursion. 当然这是一个简单的版本,通过简单的重写来完全避免递归,甚至可能不应该手动实现,但我在这里使用的特定转换是一个可以应用于任何可以进行尾递归的函数的转换。你需要尾递归的地方。 The comment should make sure that the reader can still easily spot what's going on. 评论应该确保读者仍然可以轻松发现正在发生的事情。

With GCC you can add a runtime check using the backtrace() function: 使用GCC,您可以使用backtrace()函数添加运行时检查:

#include <cassert>
#include <iostream>
#include <execinfo.h>

size_t start;

size_t stack_frames()
{
  void *array[16];
  size_t size = backtrace(array, 16);

  // std::cout << "Obtained " << size << " stack frames.\n";
  return size;
}

bool missing_tail()
{
  return stack_frames() > start + 2;
}

int bitcount(unsigned int n, int acc = 0)
{
  assert(!missing_tail());

  if (n == 0)
    return acc;

  return bitcount(n >> 1, acc + (n & 1));
}

int main()
{
  start = stack_frames();

  std::cout << bitcount(10) << '\n';

  return 0;
}

When compiled with a optimization level lower than -O2 (no tail recursion optimization) you get an assertion failure. 当编译时优化级别低于-O2 (无尾递归优化)时,会出现断言失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM