繁体   English   中英

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

[英]Is tail call optimization applicable to this function?

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

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)。 但是,如果堆栈大小和布局完全不同,并且被调用方可能是具有不同堆栈布局的多个不同功能之一,那么会发生什么?

首先,是尾递归吗? (现在,我知道可以对尾部“调用”进行优化,但不是尾部“递归”。)其次,诸如gcc,clang等主要编译器会优化这种尾部调用吗? (答案似乎是。)

如果被调用者(第二个代码示例中的foo)复杂得多怎么办? 如果被叫方和主叫方互相调用(相互递归),那么在我的第二个代码示例中,该调用是否会优化尾部调用? 如果被调用方(例如foo)是一个复杂的递归调用,它绝对不是尾部递归,并且对于编译器来说很难将其缩减为一个循环左右,那么它是否仍会进行尾部调用优化?

术语“尾递归”是呼叫站点的本地属性。 完全不受同一方法中其他调用的影响。

粗略地讲,如果在返回和封闭方法之间无需执行任何可执行代码,则该调用为尾部递归。

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

但是请注意,如果您说

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

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

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

是的,尾递归优化适用于您的示例。 请查看汇编器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

暂无
暂无

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

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