簡體   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