簡體   English   中英

防止在C#中創建堆棧框架

[英]Prevent stackframe being created in C#

我只是在閱讀csharp中的遞歸函數的性能。 我讀到,由於每次調用都會創建堆棧幀,因此遞歸函數很昂貴。

有沒有一種方法可以防止在csharp中創建堆棧幀? 我環顧四周,但似乎找不到任何建議可以抑制特定調用的堆棧幀。

我希望可以在代碼中添加一些內容,也許是如下所示的屬性:

[SurpressStackFrame()]
find(int id, Node currentNode) { 

   if(currentNode.id == id) {
      return true;
   }
   return find(id, currentNode.child);
}

(ps,我知道在這個例子中我沒有看多個孩子,這只是假設)。

在大多數情況下,創建的堆棧幀不是代碼的性能瓶頸。 但是,如果該方法具有較高的遞歸水平,則可能要避免擴大堆棧。 這稱為尾調用優化 ,是通過在調用內部方法之前釋放當前堆棧幀來完成的。

在您的特定示例中,可以將尾調用應用於return語句,因為內部方法調用的返回值會立即返回給調用者,因此不需要單獨的堆棧框架。

唯一的問題是C#目前不支持尾調用(目前可能在不久的將來不支持)。 但是,CLR確實支持它,因此,如果可以的話,您可以選擇像F#這樣的語言,它更專注於遞歸。

還有其他幾個選項供您選擇-使用CIL動態創建一個在末尾進行尾部調用的方法(但是,我不確定調用動態方法的開銷是否比在C#中調用普通的遞歸方法更好)。

在這種情況下最好的選擇-完全不使用遞歸。 您提供的方法可以很容易地被重寫,而無需使用任何遞歸,當然,即使不是全部,大多數遞歸方法也可以通過以下方式重寫:

bool find(int id, Node currentNode)
{ 
    while(currentNode.id != id)
    {
        currentNode = currentNode.child;
    }
    return true;
}

這稱為尾叫 .NET運行時支持它(因為F#需要它),但是C#編譯器(甚至是“新的” Roslyn編譯器)不支持這樣做。 請參閱github上的請求

您正在尋找的被稱為尾叫。

有一條尾巴。 IL中的指令,但C#編譯器從不使用它。 無論如何,這只是對JIT編譯器的提示,並且JIT編譯器足夠聰明,可以在可能的情況下將常規的遞歸方法調用編譯為尾調用(在您發布的示例中應該是可能的)。 與其他JIT優化一樣,它僅在發行版本中發生,而不在調試中發生。

您可以進行一個簡單的測試:

class Program
{
    static void Main()
    {
        TailCall(0);
    }
    private static void TailCall(int i)
    {
        Console.WriteLine(i);
        TailCall(++i);
    }
}

在調試中,它將引發StackOverflowException,在Release中,它將無限旋轉。

您可以在此博客文章中找到有關.NET中的尾部調用優化的更多信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM