代码之家  ›  专栏  ›  技术社区  ›  Selman Genç

为什么编译器不能通过内联优化闭包变量?

  •  8
  • Selman Genç  · 技术社区  · 6 年前

    我有一个 Main

    static void Main(string[] args)
    {
         var b = new byte[1024 * 1024];
    
         Func<double> f = () =>
         {
             new Random().NextBytes(b);
             return b.Cast<int>().Average();
         };
    
         var avg = f();
         Console.WriteLine(avg);
    }
    

    b 在这里,编译器创建一个类来捕获该变量并 b类 b类 与编译器生成的类的生存时间一样长,并且会导致内存泄漏。即使 超出范围(可能不是在这种情况下,但假设这是在另一个方法中,而不是 主要 ),字节数组将不会被释放。

    我想知道的是,既然我没有访问或修改 b类 Func ,为什么编译器不能内联该局部变量而不用创建类呢?这样地:

    Func<double> f = () =>
    {
        var b = new byte[1024 * 1024];
        new Random().NextBytes(b);
        return b.Cast<int>().Average();
    };
    

    我在调试和发布模式下编译了这段代码 DisplayClass 在以下两种情况下生成:

    enter image description here

    这只是没有实现为一个优化或有什么我错过了?

    1 回复  |  直到 6 年前
        1
  •  12
  •   Eric Lippert    6 年前

    这只是没有实现为一个优化或有什么我错过了?

    对于 具体的 例如,您可能不想进行代码转换,因为它会更改程序的语义。如果 new

    然而,更普遍的观点是密切相关的。如果您知道闭包变量仅用于其值,那么可以进行许多优化。

    旁白:最简单的更复杂的场景是:我们有两个lambda转换为委托;一个存储在短期变量中,在包含对昂贵对象的引用的局部上关闭;一个存储在长期变量中,在引用廉价对象的局部上关闭。昂贵的对象的寿命与长寿命变量一样长,即使它没有被使用。更一般地说,多个闭包可以构造为基于闭上关系的分区;当时我们只基于嵌套对闭包进行分区;相同嵌套级别的闭包是一个闭包。给定的场景是罕见的,并且有明显的解决方法,但是如果它根本没有发生就好了。

    我们之所以没有这样做,是因为在我们实施Roslyn的过程中有更重要的优化和特性,我们不想给已经很长的时间表增加风险。

    我们可以自信地执行这样的优化,因为在C语言中,很容易知道本地何时使用了别名,因此您可以确定是否在创建闭包后写入了它。

    我不知道这些优化是否同时得到了实施;很可能没有。