尾调用优化是否适用于此功能?

时间:2018-03-21 02:49:08

标签: c++ c recursion tail-recursion

如果bar调用bar(i / 2),如果i是偶数整数,则为bar(3 * i + 1),否则递归函数栏将进行尾递归。

const int bar(const int i) 
{
  if (i < 2) return i;
  return i % 2 ? bar(i/2) : bar(3*i + 1);
}

但是,如果bar调用bar或foo,那么从bar调用一组完全不同的局部变量会怎么样?

const int bar(const int i) 
{
  if (i < 2) return i;
  return i % 2 ? bar(i/2) : foo(3*i + 1);
  // where foo is very complicated recursive call that has 
  // 11 different user-defined/primitive type of
  // local variables that can't be optimized out
}

我的理解是尾递归优化将使用调用者的堆栈。在调用被调用者之前,调用者已经完成了本地变量。因此,被调用者可以重用它。当调用者和被调用者具有相同的功能时,我的理解听起来很好(例如,foo调用foo,bar调用bar)。但是,如果堆栈大小和布局完全不同,并且callees可能是具有不同堆栈布局的多个不同函数之一,那么会发生什么?

首先,它是尾递归吗? (现在,我明白可以应用尾部“调用”优化,但这不是尾部“递归”。)其次,主要的编译器,如gcc,clang等,会优化这种尾调用? (答案似乎是肯定的。)

如果被调用者(第二个代码示例中的foo)要复杂得多怎么办?如果被调用者和调用者正在相互调用(相互递归),那么我的第二个代码示例中的调用是否会进行尾调用优化?如果被调用者(例如foo)是一个复杂的递归调用,绝对不是尾递归,并且编译器很难将它减少到循环左右,它是否仍然是尾调用优化的?

2 个答案:

答案 0 :(得分:4)

术语&#34;尾递归&#34;是呼叫站点的本地属性。它在同一方法中的其他调用完全没有受到影响。

粗略地说,如果在返回和返回封闭方法之间不需要运行可执行代码,则调用是尾递归的。

因此,您示例中对bar()的所有调用都是尾递归的。

但请注意,如果你说

return i % 2 ? bar(i/2) : 1 + bar(3*i + 1);

然后第一个调用是尾递归的,但第二个调用不是因为添加1 +必须在返回后执行。

答案 1 :(得分:2)

  

尾递归优化是否适用于此函数?

是的,尾递归优化适用于您的示例。请查看第二个样本的汇编程序https://godbolt.org/g/cSpUZw。应用的渐进式优化。递归被循环替换。

bar(int):
  cmp edi, 1
  jg .L12
  jmp .L6
.L15:
  sar edi
  cmp edi, 1
  je .L14
.L12:
  test dil, 1
  jne .L15
  lea edi, [rdi+1+rdi*2]
  jmp foo(int)
.L14:
  mov eax, 1
  ret
.L6:
  mov eax, edi
  ret