c# - 用 IEnumerable/yield 解释这种奇怪的行为

标签 c# stack-overflow jit yield

猜猜……当 i == 0 时,这个程序需要多长时间才能产生第一个输出?应该是即时的吧?通过对 yield 的延迟评估,它应该在那之后快速连续地产生输出,对吗?

static void Main(string[] args)
{
   Stopwatch stopwatch = Stopwatch.StartNew();
   int i = 0;
   foreach (var item in massiveYieldStatement())
   {
        if (i++ % 10000 == 0) 
           Console.WriteLine(stopwatch.ElapsedMilliseconds / 1000);
   }
   Console.ReadKey();
}

static IEnumerable<string> massiveYieldStatement()
{
   yield return "a"; 
   yield return "a";

   .. repeat 200,000 times !!

   yield return "a";
}

但事实并非如此!它会在 4 到 21 分钟内没有输出,然后很快完成 - 在一个案例中不到 60 毫秒!在那几分钟内,100% 的一个核心 CPU 被使用,并且内存使用量增加。在我遇到这种情况的实际场景中,甚至在第一次迭代发生之前就抛出了 Stackoverflow 异常!我已经在 Visual Studio 的 Debug模式下和命令提示符下的 Release模式下进行了尝试。我已经在 Windows 7 x64 和 Windows Server 2008 R2 x64 上试过了。

谁能解释一下这里发生了什么?它适合你吗?

注意:这不是真实代码:真实代码的 yield 语句要少得多,但要复杂得多。这只是最简单的复制。

最佳答案

此代码生成的程序集有几 MB 大。 yield return 是一个特殊的野兽,因为它看似简单,但 C# 编译器实际上会生成一个类(“状态机”)来实现 massiveYieldStatement 方法。我很确定您正在等待 JIT 编译器编译该类的 MoveNext() 方法(您可以使用 ildasm 验证这一点:如果您尝试打开 MoveNext() 方法,它也会花费很多时间).

关于c# - 用 IEnumerable/yield 解释这种奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8253836/

相关文章:

c# - 为什么 System.Type.GetHashCode 会为所有实例和类型返回相同的值?

recursion - 提前编译时 Clojure fn 名称泄漏到其范围之外

java - 合并排序算法中的堆栈溢出错误?

clr - 为什么具有 PE​​Verified 堆栈溢出场景 (maxstack) 的程序不会使 CLR 崩溃?

android - 由于 JIT,BouncycaSTLe AES 256 多线程解密速度下降

java - JVM JIT 编译器如何优化 "duplicated"Java 代码?

c# - ASP.NET Core 中间件出现异常时得到空响应

c# - 如何使用 Active Directory 通用身份验证打开 System.Data.SQLClient.SQLConnection

c# - 使用 Excel = Microsoft.Office.Interop.Excel 编译错误

python - 为什么 numba 不提高我的背包功能的速度?