2017-01-21 41 views
2

以下两段代码中的哪一段在不同情况下表现更好,为什么?GetOrAdd新vs工厂绩效

private readonly ConcurrentDictionary<int, List<T>> _coll; 
_coll.GetOrAdd(1, new List<T>()); 

这将创建上不需要即使每次调用一个新的List(多少钱,如果我们通过capacity为0这个说法还是有关系吗?)。

private readonly ConcurrentDictionary<int, List<T>> _coll; 
_coll.GetOrAdd(1, (val) => new List<T>()); 

这只会造成对需求的List,但有一个委托调用。

回答

4

就内存而言,第一种方法是每次都会产生一个分配,而第二种方法会使用缓存的委托对象,因为它不捕获任何变量。编译器负责处理缓存委托的生成。在容量设置为零的第一种情况中没有区别,因为List<T>的默认构造函数在初始化时使用空数组,与显式容量为0相同。

在执行指令方面,它们在因为没有使用第二个参数,因此找到了键。如果找不到密钥,第一种方法只需读取局部变量,而第二种方式将具有间接层来调用委托。另外,looking into the source code,似乎与工厂的GetOrAdd将做一个额外的查找(通过TryGetValue),以避免调用工厂。代表也可能会被执行多次。 GetOrAdd只保证你在字典中看到一个条目,而不是只调用一次工厂。

总之,如果通常找不到关键字,并且通过委托没有间接关系,则第一种方式可能更具性能。但是,如果通常找到密钥,则第二种方式更高性能,因为分配更少。对于缓存中的实现,你通常会期望有很多命中,所以如果这是这样的话,我会推荐第二种方法。实际上,两者之间的差异取决于整个应用程序对此代码路径中的分配有多敏感。

此外,无论使用此的实现将可能需要实现锁定围绕List<T>由于它不是线程安全返回。

+0

我以'List '为例,其实际的集合是线程安全的。另外,从约瑟夫的建议中可以看出,'()=> Enumerable.Empty ()有助于表现吗? – Hele

+1

此外,它似乎取决于正在使用什么框架版本(以及是否实施了新行为),就像.NET 4.5中的情况一样,其中更改具有一些不良副作用。请参阅https://basildoncoder.com/blog/concurrentdictionary-getoradd-vs.html – Alex

+0

@ Hele如果您打算使用Enumerable.Empty (),那么不需要委托。'Enumerable.Empty ()'只是读取静态只读字段。不过,我不确定为什么要使用像IEnumerable 这样的只读界面。我怀疑它会使更新更难,但我需要看到更多的代码。 –

0

我无法想象你会看到很大的性能差异,除非你正在处理一个非常大的数据集。这也取决于你的每件物品被击中的可能性。泛型在运行时级别上进行了极其优化,使用委托可以以任何方式分配结果。

我的建议是使用Enumerable.Empty<T>(),因为您将保存自己在每个数组项目上的分配。