2016-08-03 12 views
1

Я видел this question сегодня о некоторой разнице в производительности в отношении ConcurrentDictionary методов, и я видел ее как преждевременную микро-оптимизацию.C# лямбда-распределение и сбор

Однако, по некоторым соображениям, я понял (если не ошибаюсь), что каждый раз, когда мы передаем лямбду методу, 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)); 
    
  2. Lambda с крышкой:

    // 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)); 
    

Третий случай, очевидно, распределение свободных, второй будет необходимо выделение.

Но это первый случай (лямбда, который не имеет захвата) полностью не содержит свободных (по крайней мере, в новых версиях CLR)? Кроме того, является ли эта деталь реализацией среды выполнения или что-то, указанное стандартом?

ответ

1

Прежде всего, CLR не знает, что такое лямбда. Это концепция C#. Он скомпилирован. Язык C# предоставляет вам значение делегата, где вы написали лямбда.

C# не гарантирует, что экземпляр делегата (или базовый метод) является общим или нет. На самом деле, я считаю, что инициализация общих делегатов лямбды является небезопасной и небезопасной. Поэтому в зависимости от времени вы можете увидеть только один или несколько экземпляров делегата.

Так что это деталь реализации языка.

На практике вы можете полагаться на общие формы 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"); 
    } 
} 

Если вы хотите, чтобы проверить, что происходит в каждом случае (статический \ поле экземпляра, местные жители, это, Shared генерируемые объекты)

взглянуть на C# specification, раздел пример 6.5.3 Реализация из Анонимные функции