我今天看到this question关于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版本中)?此外,这是运行时的实现细节,还是标准指定的内容?
答案 0 :(得分:3)
首先,CLR不知道lambda是什么。这是一个C#概念。它被编译掉了。 C#语言为您提供了编写lambda的委托值。
C#不保证委托实例(或底层方法)是否共享。事实上,我认为共享lambda委托的初始化是线程不安全和racy。因此,根据时间安排,您可能只看到一个或多个委托实例。
所以它是该语言的实现细节。
在实践中,您可以依赖共享的表单1和表单3。这对性能很重要。如果不是这种情况,我认为它将被视为一个高优先级的错误。
答案 1 :(得分:0)
如果通过分配来表示生成的public class ThreeSum
{
public static int count(int[] a)
{ // Count triples that sum to 0.
int N = a.length;
int cnt = 0;
for (int i = 0; i < N; i++)
for (int j = i+1; j < N; j++)
for (int k = j+1; k < N; k++)
if (a[i] + a[j] + a[k] == 0)
cnt++;
return cnt;
}
public static void main(String[] args)
{
int[] a = In.readInts(args[0]); // error here
StdOut.println(count(a)); // and here
}
}
,那么第一种情况将是免费分配。但它仍然需要一些分配,例如In.readInts
。
6.5.3实施例
DisplayClass
匿名函数的最简单形式是不捕获外部变量的函数:
Func<Key, Value>
这可以转换为委托实例化,该实例化引用编译器生成的静态方法,其中放置匿名函数的代码:
public delegate void D();
如果你想检查每种情况下发生的事情,(静态\实例字段,本地人,这个,共享生成的对象)
查看C# specification,匿名函数的 6.5.3实现示例