在方法开始时发生C#关闭堆分配 [英] C# closure heap allocation happening at start of method

查看:43
本文介绍了在方法开始时发生C#关闭堆分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我似乎遇到了C#编译器的某些奇怪行为.

I seem to have run into some odd behavior of the C# compiler.

请考虑以下代码示例:

static void Main(string[] args)
{
    Foo(false, 8);
}

public static void Foo(bool execute, int x)
{
    if (execute)
    {
        Task.Run(() => Console.WriteLine(x));
    }
}

运行此命令(在发行版中)表明发生了一些意外的分配.检查IL表明,由闭包触发的堆分配出现在函数的最开始,而不是在条件内部:

Running this (in release) shows some unexpected allocations happening. Examining the IL shows that that the heap allocation triggered by the closure appears at the very start of the function, rather than inside the condition:

  .method public hidebysig static void 
    Foo(
      bool execute, 
      int32 x
    ) cil managed 
  {
    .maxstack 2
    .locals init (
      [0] class Test.Program/'<>c__DisplayClass1_0' 'CS$<>8__locals0'
    )

    IL_0000: newobj       instance void Test.Program/'<>c__DisplayClass1_0'::.ctor()
    IL_0005: stloc.0      // 'CS$<>8__locals0'
    IL_0006: ldloc.0      // 'CS$<>8__locals0'
    IL_0007: ldarg.1      // x
    IL_0008: stfld        int32 Test.Program/'<>c__DisplayClass1_0'::x

    // [18 13 - 18 25]
    IL_000d: ldarg.0      // execute
    IL_000e: brfalse.s    IL_0022

    // [20 17 - 20 54]
    IL_0010: ldloc.0      // 'CS$<>8__locals0'
    IL_0011: ldftn        instance void Test.Program/'<>c__DisplayClass1_0'::'<Foo>b__0'()
    IL_0017: newobj       instance void [mscorlib]System.Action::.ctor(object, native int)
    IL_001c: call         class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Run(class [mscorlib]System.Action)
    IL_0021: pop          

    // [22 9 - 22 10]
    IL_0022: ret          

  } // end of method Program::Foo

我在这里错过了什么吗,有人对这种奇怪的行为有一个解释吗?Roslyn是否有可能生成为闭包分配的代码,而不管我们是否实际执行它们?

Am I missing something here, does anyone has an explanation for this strange behavior? Is it possible that Roslyn generates code which allocates for closures regardless of whether we actually execute them?

推荐答案

此行为是设计使然.

当您的方法具有闭包时,闭包内使用的所有变量都必须是闭包类的一部分(以便lambda可以访问其当前值).

When your method has a closure, all variables used inside the closure must be part of the closure class (so that the lambda can access their current values).

如果编译器没有立即分配闭包,则在创建闭包实例时,必须将值从局部变量复制到闭包类中的字段,这会浪费时间和内存.

Had the compiler not allocated the closure immediately, it would have to copy the values from local variables to fields on the closure class when the closure instance is created, wasting time and memory.

如果多个具有不同可达性(或更糟的是嵌套作用域)的lambda封闭相同的变量,这也将使代码生成器更加危险,并且更加复杂.

That would also make the codegen much riskier and more complicated if multiple lambdas with different reachabilities (or, worse, nested scopes) close over the same variables.

这篇关于在方法开始时发生C#关闭堆分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆