C# lambda 分配和收集

Lou*_*Lou 6 .net clr lambda garbage-collection micro-optimization

我今天看到了这个关于ConcurrentDictionary方法的一些性能差异的问题,我认为这是一个过早的微优化。

然而,经过一番思考,我意识到(如果我没记错的话),每次我们将 lambda 传递给一个方法时,CLR 都需要分配内存,传递适当的闭包(如果需要),然后过一段时间再收集它.

有以下三种可能:

  1. 没有闭包的 Lambda:

    // the lambda should internally compile to a static method, 
    // but will CLR instantiate a new ManagedDelegate wrapper or
    // something like that?
    return concurrent_dict.GetOrAdd(key, k => ValueFactory(k));
    
    Run Code Online (Sandbox Code Playgroud)
  2. 带闭包的 Lambda:

    // this is definitely an allocation
    return concurrent_dict.GetOrAdd(key, k => ValueFactory(k, stuff));
    
    Run Code Online (Sandbox Code Playgroud)
  3. 外部检查(例如检查锁定前的条件):

    // no lambdas in the hot path
    if (!concurrent_dict.TryGetValue(key, out value))
        return concurrent_dict.GetOrAdd(key, k => ValueFactory(k));
    
    Run Code Online (Sandbox Code Playgroud)

第三种情况显然不需要分配,第二种情况需要分配。

但是第一种情况(没有捕获的 lambda)是否完全不需要分配(至少在较新的 CLR 版本中)?另外,这是运行时的实现细节,还是标准指定的内容?

usr*_*usr 4

首先,CLR 不知道 lambda 是什么。这是一个 C# 概念。它被编译掉了。C# 语言为您提供了编写 lambda 的委托值。

C# 不保证委托实例(或底层方法)是否共享。事实上,我相信共享 lambda 委托的初始化是线程不安全且不安全的。因此,根据时间的不同,您可能只会看到一个或多个委托实例。

所以这是该语言的实现细节。

实际上,您可以依赖共享的表格 1 和 3。这对于性能来说很重要。如果不是这种情况,我认为它将被视为高优先级错误。