这个问题已经在这里有了答案:




9年前关闭。






请使用以下C#代码:

using System;

namespace TailTest
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            Counter(0);
        }

        static void Counter(int i)
        {
            Console.WriteLine(i);
            if (i < int.MaxValue) Counter(++i);
        }
    }
}

C#编译器(无论如何还是我的)都会将Counter方法编译为以下CIL:
.method private static hidebysig default void Counter (int32 i) cil managed
{
.maxstack 8
IL_0000:  ldarg.0
IL_0001:  call void class [mscorlib]System.Console::WriteLine(int32)
IL_0006:  ldarg.0
IL_0007:  ldc.i4 2147483647
IL_000c:  bge IL_0019
IL_0011:  ldarg.0
IL_0012:  ldc.i4.1
IL_0013:  add
IL_0014:  call void class TailTest.MainClass::Counter(int32)
IL_0019:  ret
}

上面的代码的问题是,它将导致堆栈溢出(在我的硬件上大约为i = 262000)。为了解决这个问题,某些语言执行了所谓的尾音消除或尾音优化(TCO)。本质上,它们将递归调用变成一个循环。

我的理解是.NET 4 JIT的64位实现现在可以执行TCO,并且可以避免上述CIL之类的代码溢出。但是,32位JIT不会。单声道似乎也没有。有趣的是,JIT(在时间和资源压力下)执行TCO,而C#编译器则不这样做。我的问题是,为什么C#编译器本身不更了解TCO?

有一条CIL指令告诉JIT执行TCO。例如,C#编译器可以代替生成以下CIL:
.method private static hidebysig default void Counter (int32 i) cil managed
{
.maxstack 8
IL_0000:  ldarg.0
IL_0001:  call void class [mscorlib]System.Console::WriteLine(int32)
IL_0006:  ldarg.0
IL_0007:  ldc.i4 2147483647
IL_000c:  bge IL_001c
IL_0011:  ldarg.0
IL_0012:  ldc.i4.1
IL_0013:  add
IL_0014:  tail.
IL_0017:  call void class TailTest.MainClass::Counter(int32)
IL_001c:  ret
}

与原始代码不同,即使在32位JIT(.NET和Mono)上,该代码也不会溢出并且可以运行到完整状态。神奇之处在于tail. CIL指令。像F#这样的编译器会自动生成包含此指令的CIL。

所以我的问题是,C#编译器不执行此操作有技术原因吗?

我知道,从历史上看,这也许不值得。在惯用的C#和/或.NET框架中,像Counter()这样的代码并不常见。您可以轻松地将C#的TCO视为不必要或过早的优化。

随着LINQ等的引入,似乎C#和C#开发人员都朝着更多的功能方向发展。因此,如果使用递归不是一件不安全的事情,那将是很好的。但是我的问题确实是一个技术性更高的问题。

缺少使TCO这样的东西对C#来说不是一个好主意(或冒险)的东西。还是有什么事情使正确做事特别棘手?这确实是我希望了解的。有见识吗?

编辑:非常感谢您提供的信息。我只是想明确一点,我不是在批评缺少甚至不要求此功能。我对围绕优先级排序的合理性并不 super 感兴趣。我的好奇心是我可能无法理解或理解的哪些障碍使这件事变得困难,危险或令人讨厌。

也许不同的背景将有助于使谈话重点...

假设我要在CLR上实现自己的类似于C#的语言。我为什么不(机会成本除外)包括“尾部”的自动透明排放。说明在适当的地方?在非常类似于C#的语言中支持此功能时,我会遇到什么挑战或遇到什么限制。

再次(并提前)感谢您的答复。

最佳答案

检查以下链接

Why doesn't .NET/C# optimize for tail-call recursion?
/491463#491463
http://social.msdn.microsoft.com/Forums/en-US/netfxtoolsdev/thread/67b6d908-8811-430f-bc84-0081f4393336?StatusCode=1
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=166013&wa=wsignin1.0

以下声明是MS官方的(Luke Hoban Visual C#编译器程序管理器),并从最后一个链接复制而来

关于c# - C#不发出 “tail.” CIL指令是出于技术原因吗? [复制],我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7096157/

10-09 01:19