2016-08-03 87 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實現示例匿名函數