Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 防止在C中创建stackframe#_C# - Fatal编程技术网

C# 防止在C中创建stackframe#

C# 防止在C中创建stackframe#,c#,C#,我只是在读csharp中递归函数的性能。我读到递归函数很昂贵,因为每次调用时都会创建堆栈帧 有没有办法防止在csharp中创建堆栈帧?我环顾四周,但似乎找不到任何迹象表明堆栈帧可以被抑制用于特定调用 我希望我可以在代码中添加一些东西,可能是一个如下属性: [SurpressStackFrame()] find(int id, Node currentNode) { if(currentNode.id == id) { return true; } return

我只是在读csharp中递归函数的性能。我读到递归函数很昂贵,因为每次调用时都会创建堆栈帧

有没有办法防止在csharp中创建堆栈帧?我环顾四周,但似乎找不到任何迹象表明堆栈帧可以被抑制用于特定调用

我希望我可以在代码中添加一些东西,可能是一个如下属性:

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

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

(顺便说一句,我知道在这个例子中我不是在看多个孩子,这只是假设)。

它被称为。NET运行时支持它(因为F#需要它),但C#编译器(甚至是“新的”Roslyn编译器)不支持它。请参阅。

它被称为。NET运行时支持它(因为F#需要它),但C#编译器(甚至是“新的”Roslyn编译器)不支持它。请参阅。

在大多数情况下,正在创建的堆栈帧不是代码的性能瓶颈。但是,如果方法中存在高级别的递归,则可能希望避免扩大堆栈。这称为尾部调用优化,通过在调用内部方法之前释放当前堆栈帧来完成

在您的特定示例中,尾部调用可以应用于
return
语句,因为内部方法调用的返回值会立即返回给调用方,因此不需要单独的堆栈框架

唯一的问题是,C#不支持尾部调用(目前,可能在不久的将来也不支持)。但是,CLR确实支持它,因此如果可以,可以选择像F#这样更注重递归的语言

您还可以使用其他几个选项—使用CIL动态创建一个方法,该方法在最后进行尾部调用(但是,我不确定调用动态方法的代价是否比在C#中调用普通递归方法更好)

在这种情况下,最好的选择是根本不要使用递归。您提供的方法可以很容易地重写,而无需使用递归,当然,大多数(如果不是所有)递归方法都可以这样重写:

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

在大多数情况下,正在创建的堆栈帧不是代码的性能瓶颈。但是,如果方法中存在高级别的递归,则可能希望避免扩大堆栈。这称为尾部调用优化,通过在调用内部方法之前释放当前堆栈帧来完成

在您的特定示例中,尾部调用可以应用于
return
语句,因为内部方法调用的返回值会立即返回给调用方,因此不需要单独的堆栈框架

唯一的问题是,C#不支持尾部调用(目前,可能在不久的将来也不支持)。但是,CLR确实支持它,因此如果可以,可以选择像F#这样更注重递归的语言

您还可以使用其他几个选项—使用CIL动态创建一个方法,该方法在最后进行尾部调用(但是,我不确定调用动态方法的代价是否比在C#中调用普通递归方法更好)

在这种情况下,最好的选择是根本不要使用递归。您提供的方法可以很容易地重写,而无需使用递归,当然,大多数(如果不是所有)递归方法都可以这样重写:

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

你要找的是叫尾叫尾叫

有一条尾巴。指令,但C#编译器从不使用它。总之,这只是JIT编译器的提示,JIT编译器足够聪明,可以在可能的情况下编译常规递归方法调用作为尾部调用(在您发布的示例中应该是可能的)。与其他JIT优化一样,它只发生在发布版本中,而不发生在调试中

您可以进行一个简单的测试:

class Program
{
    static void Main()
    {
        TailCall(0);
    }
    private static void TailCall(int i)
    {
        Console.WriteLine(i);
        TailCall(++i);
    }
}
在调试中,它将抛出StackOverflowException,在发布时它将无限旋转


您可以在中的.NET中找到有关尾部调用优化的更多信息。

您正在寻找的是所谓的尾部调用

有一条尾巴。指令,但C#编译器从不使用它。无论如何,这只是对JIT编译器的一个提示,JIT编译器足够聪明,可以在可能的情况下将常规递归方法调用编译为尾部调用(在您发布的示例中应该是可能的)。与其他JIT优化一样,它只发生在发布版本中,而不发生在调试中

您可以进行一个简单的测试:

class Program
{
    static void Main()
    {
        TailCall(0);
    }
    private static void TailCall(int i)
    {
        Console.WriteLine(i);
        TailCall(++i);
    }
}
在调试中,它将抛出StackOverflowException,在发布时它将无限旋转


您可以在中的.NET中找到有关尾部调用优化的更多信息。

它被称为您想要的尾部调用递归。NET(运行时支持它)。如果您关心性能,为什么不将其重写为一个迭代函数呢。@CSharpie我只是想知道是否有一种方法可以“吃我的蛋糕,吃它”。)这就是所谓的尾部调用递归。NET(运行时支持它)。如果您关心性能,为什么不将其重写为一个迭代函数呢。@CSharpie我只是想知道是否有一种方法可以“吃我的蛋糕,吃它”。)谢谢,我不知道它的术语。但是如果JIT认为合适的话,它仍然会这样做,对吗?即使没有显式的.tail调用。@Evk是的,
.tail
只是一个提示。。。JIT甚至应该调用其他方法。。。我不知道它有多积极。谢谢,我不知道它的术语。但是如果JIT认为合适的话,它仍然会这样做,对吗?即使没有显式的.tail调用。@Evk是的,
.tail
只是一个提示。。。JIT甚至应该调用其他方法。。。我不知道它有多积极。