C# lambda 分配和收集
C# lambda allocation and collection
我今天看到 关于 ConcurrentDictionary
方法的一些性能差异,我认为这是过早的微优化。
然而,经过一番思考,我意识到(如果我没记错的话),每次我们将 lambda 传递给方法时,CLR 都需要分配内存,传递适当的闭包(如果需要),然后过段时间再收吧。
三种可能:
没有闭包的 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));
带闭包的 Lambda:
// this is definitely an allocation
return concurrent_dict.GetOrAdd(key, k => ValueFactory(k, stuff));
外部检查(如锁定前检查条件):
// no lambdas in the hot path
if (!concurrent_dict.TryGetValue(key, out value))
return concurrent_dict.GetOrAdd(key, k => ValueFactory(k));
第三种情况显然是免分配的,第二种则需要分配。
但是第一种情况(没有捕获的 lambda)是否完全没有分配(至少在较新的 CLR 版本中)?另外,这是运行时的实现细节,还是标准规定的?
如果分配是指生成的 DisplayClass
,那么第一种情况将是免费分配。但它仍然需要一些分配,例如Func<Key, Value>
。
6.5.3 Implementation example
public delegate void D();
The simplest form of an anonymous function is one that captures no outer variables:
class Test
{
static void F() {
D d = () => { Console.WriteLine("test"); };
}
}
This can be translated to a delegate instantiation that references a compiler generated static method in which the code of the anonymous function is placed:
class Test
{
static void F() {
D d = new D(__Method1);
}
static void __Method1() {
Console.WriteLine("test");
}
}
如果您想检查每种情况下发生了什么,(static\instance 字段、本地、this、共享生成的对象)
看看 C# specification 节 6.5.3 匿名函数的实现示例
首先,CLR 不知道 lambda 是什么。这是一个 C# 概念。它被编译掉了。 C# 语言为您提供了一个委托值,您可以在其中编写 lambda。
C# 不保证委托实例(或底层方法)是否共享。事实上,我相信共享 lambda 委托的初始化是线程不安全和活泼的。因此,根据时间安排,您可能只会看到一个或多个委托实例。
所以这是语言的实现细节。
实际上,您可以依赖共享的表格 1 和 3。这对性能很重要。如果不是这种情况,我认为它会被视为高优先级错误。
我今天看到 ConcurrentDictionary
方法的一些性能差异,我认为这是过早的微优化。
然而,经过一番思考,我意识到(如果我没记错的话),每次我们将 lambda 传递给方法时,CLR 都需要分配内存,传递适当的闭包(如果需要),然后过段时间再收吧。
三种可能:
没有闭包的 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));
带闭包的 Lambda:
// this is definitely an allocation return concurrent_dict.GetOrAdd(key, k => ValueFactory(k, stuff));
外部检查(如锁定前检查条件):
// no lambdas in the hot path if (!concurrent_dict.TryGetValue(key, out value)) return concurrent_dict.GetOrAdd(key, k => ValueFactory(k));
第三种情况显然是免分配的,第二种则需要分配。
但是第一种情况(没有捕获的 lambda)是否完全没有分配(至少在较新的 CLR 版本中)?另外,这是运行时的实现细节,还是标准规定的?
如果分配是指生成的 DisplayClass
,那么第一种情况将是免费分配。但它仍然需要一些分配,例如Func<Key, Value>
。
6.5.3 Implementation example
public delegate void D();
The simplest form of an anonymous function is one that captures no outer variables:
class Test { static void F() { D d = () => { Console.WriteLine("test"); }; } }
This can be translated to a delegate instantiation that references a compiler generated static method in which the code of the anonymous function is placed:
class Test { static void F() { D d = new D(__Method1); } static void __Method1() { Console.WriteLine("test"); } }
如果您想检查每种情况下发生了什么,(static\instance 字段、本地、this、共享生成的对象)
看看 C# specification 节 6.5.3 匿名函数的实现示例
首先,CLR 不知道 lambda 是什么。这是一个 C# 概念。它被编译掉了。 C# 语言为您提供了一个委托值,您可以在其中编写 lambda。
C# 不保证委托实例(或底层方法)是否共享。事实上,我相信共享 lambda 委托的初始化是线程不安全和活泼的。因此,根据时间安排,您可能只会看到一个或多个委托实例。
所以这是语言的实现细节。
实际上,您可以依赖共享的表格 1 和 3。这对性能很重要。如果不是这种情况,我认为它会被视为高优先级错误。