繁体   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