简体   繁体   English

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

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

If bar calls bar(i/2) if i is an even integer and bar(3*i + 1) otherwise, the recursive function bar would tail-recursion. 如果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);
}

However, what if bar calls either bar or foo, who has a completely different set of local variables from bar? 但是,如果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
}

My understanding is tail recursion optimization will use caller's stack. 我的理解是尾部递归优化将使用调用方的堆栈。 The caller is already done with its local variables before calling the callee. 在调用被调用方之前,调用方已经使用其局部变量完成了操作。 So, the callee can reuse it. 因此,被调用者可以重用它。 My understanding sounds fine when the caller and callee are the same function(eg foo calls foo, bar calls bar). 当调用方和被调用方具有相同的功能时,我的理解听起来不错(例如foo调用foo,bar调用bar)。 However, if the stack size and layout are totally different, and the callees might be one of multiple different functions with different stack layout, what is going to happen? 但是,如果堆栈大小和布局完全不同,并且被调用方可能是具有不同堆栈布局的多个不同功能之一,那么会发生什么?

Firstly, would it be tail recursion? 首先,是尾递归吗? (Now, I understand that tail "call" optimization might be applied to but not this is not tail "recursion.") Secondly, major compilers such as gcc, clang, etc, would optimize this kind of tail calls? (现在,我知道可以对尾部“调用”进行优化,但不是尾部“递归”。)其次,诸如gcc,clang等主要编译器会优化这种尾部调用吗? (The answer seems yes.) (答案似乎是。)

What if the callee (foo in the second code example) is much more complicated? 如果被调用者(第二个代码示例中的foo)复杂得多怎么办? If the callee and caller are calling each other (mutual recursion), is the call in my second code example would be tail-call-optimized? 如果被叫方和主叫方互相调用(相互递归),那么在我的第二个代码示例中,该调用是否会优化尾部调用? If the callee (eg foo) is a complicated recursive call that is definitely not a tail recursion and very hard for a compiler to reduce it to a loop or so, would it be still tail-call-optimized? 如果被调用方(例如foo)是一个复杂的递归调用,它绝对不是尾部递归,并且对于编译器来说很难将其缩减为一个循环左右,那么它是否仍会进行尾部调用优化?

The term "tail-recursive" is a local property of a call site. 术语“尾递归”是呼叫站点的本地属性。 It's not affected at all by other calls in the same method. 完全不受同一方法中其他调用的影响。

Roughly speaking, a call is tail-recursive if no executable code needs to run between its return and the return of the enclosing method. 粗略地讲,如果在返回和封闭方法之间无需执行任何可执行代码,则该调用为尾部递归。

Consequently all the calls to bar() in your example are tail-recursive. 因此,示例中对bar()所有调用都是尾递归的。

But note that if you said 但是请注意,如果您说

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

then the first call is tail-recursive, but the second isn't because the addition 1 + must execute after it returns. 那么第一个调用是尾递归调用,但是第二个调用不是因为加法1 +必须在返回后执行。

Is tail recursion optimization applicable to this function? 尾递归优化是否适用于此功能?

Yes, the tail recursion optimization is applicable in your examples. 是的,尾递归优化适用于您的示例。 Please look at assembler https://godbolt.org/g/cSpUZw for the second sample. 请查看汇编器https://godbolt.org/g/cSpUZw以获取第二个示例。 The more progressive optimization applied. 应用了更渐进的优化。 Recursion is replaced with a loop. 递归被循环替换。

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