2016-08-03 99 views
1

今天我看到this question关于ConcurrentDictionary方法的一些性能差异,我把它看作是一种过早的微观优化。但是,经过一番思考,我意识到(如果我没有弄错),每次我们将一个lambda传递给一个方法时,CLR都需要分配内存,传递适当的闭包(如果需要的话),然后收集过了一段时间。C#lambda分配和集合

有三种可能性:

  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)); 
    
  2. 拉姆达与闭合:

    // this is definitely an allocation 
    return concurrent_dict.GetOrAdd(key, k => ValueFactory(k, stuff)); 
    
  3. 外检查(如检查前的状态锁):

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

第三种情况显然是免分配的,第二种情况需要分配。

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

回答

1

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

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

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

在实践中,您可以依靠表单1和3共享。这对性能很重要。如果不是这种情况,我认为这将被视为一个高优先级的错误。

0

如果通过分配你的意思是生成DisplayClass,所以第一个案例将免费分配。但它仍然需要一些分配,例如Func<Key, Value>

6.5.3实施例

public delegate void D(); 

匿名函数的最简单的形式是一个捕获没有外变量:

class Test 
{ 
    static void F() { 
     D d =() => { Console.WriteLine("test"); }; 
    } 
} 

这可以翻译成代表实例化该引用编译器生成静态方法,其中放置匿名函数的代码:

class Test 
{ 
    static void F() { 
     D d = new D(__Method1); 
    } 
    static void __Method1() { 
     Console.WriteLine("test"); 
    } 
} 

如果您要检查在每种情况下发生了什么,(静态\实例字段,当地人,对此,共享生成的对象)

看看C# specification,科6.5.3实现示例匿名函数