GetOrAdd新的vs工厂性能

时间:2017-01-21 21:43:32

标签: c# performance concurrentdictionary

以下两段代码中哪一条在不同情况下表现更好?为什么?

1

STATIC

即使不需要,也会在每次调用时创建一个新的private readonly ConcurrentDictionary<int, List<T>> _coll; _coll.GetOrAdd(1, new List<T>()); (如果我们将List作为0传递,这个语句仍然有多重要?)。

2

capacity

这只会根据需要创建private readonly ConcurrentDictionary<int, List<T>> _coll; _coll.GetOrAdd(1, (val) => new List<T>()); ,但会有一个委托调用。

2 个答案:

答案 0 :(得分:4)

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

就执行指令而言,由于未使用第二个参数,因此在找到密钥时它们是相同的。如果找不到密钥,第一种方法只需要读取局部变量,而第二种方式将有一个间接层来调用委托。此外,looking into the source code,看起来工厂的GetOrAdd将执行额外的查找(通过TryGetValue)以避免调用工厂。代表也可能被多次执行。 GetOrAdd只是保证你在字典中看到一个条目,而不是只调用一次工厂。

总之,如果通常没有找到密钥,第一种方式可能会更高效,因为无论如何都需要进行分配,并且没有通过委托进行间接寻址。但是,如果通常找到密钥,则第二种方式更具性能,因为分配更少。对于缓存中的实现,您通常期望有大量的命中,所以如果是这样的话,我会建议第二种方式。实际上,两者之间的差异取决于整个应用程序对此代码路径中的分配的敏感程度。

此外,使用此实现的任何实现都可能需要在返回的List<T>周围实现锁定,因为它不是线程安全的。

答案 1 :(得分:0)

除非您使用的是非常大的数据集,否则我无法想象您会发现性能差异很大。这还取决于你的每件物品被击中的可能性。泛型在运行时级别进行了极好的优化,使用委托会导致分配。

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