【发布时间】:2020-05-17 05:08:11
【问题描述】:
我一直缺乏为ConcurrentDictionary<TKey, TValue> 选择适当初始容量的指导。
我的一般用例是您真正想要执行以下操作但不能执行的情况:
public static class StaticCache<T>
{
public static readonly Action CompiledExpression = ...;
}
这种基于泛型的方法避免了字典查找,但只有当我们在编译时总是知道所需的类型时才能使用。如果我们在运行时只知道Type,我们就不能再使用这种方法了。下一个竞争者是ConcurrentDictionary<TKey, TValue>。
documentation 声明:
默认容量(DEFAULT_CAPACITY),代表初始桶数,是在构建大字典时,在非常小的字典大小和调整大小的数量之间进行权衡。此外,容量不应被小素数整除。默认容量为 31。
我的预期元素数量往往相对较少。有时小到 3 或 5 个,有时可能只有 15 个。因此:
- 应用程序生命周期内的插入次数非常最少,保证 [写入] 并发级别为 1,从而优化紧凑性和读取操作。
- 最好使用尽可能小的内存占用,以优化缓存行为。
由于默认初始容量为 31,我们可以通过使用较小的初始容量来潜在地减少对缓存的影响(以及增加字典保留在缓存中的可能性)。
这引发了以下问题:
-
容量实际上是什么意思?
- (A) 字典不需要增长来容纳这么多元素?
- (B) A 的固定百分比,取决于字典的最大“丰满度”,例如75%?
- (C) A 或 B 的近似值,取决于实际内容的哈希码如何分布它们?
什么构成和不构成“小素数”?显然,31 没有。 11吗? 17吗? 23 吗?
如果我们碰巧想要一个接近小素数的容量,我们可以选择什么容量呢?我们是简单地选择最接近的非素数,还是素数更适合容量,我们真的应该选择更大的素数吗?
【问题讨论】:
-
(2) 当它说
the capacity should not be divisible by a small prime number.时,它暗示other than itself,所以31不能被一个小的素数整除,因为31 本身就是一个素数。 (3) 见我对 (2) 的评论! -
这些都是很好的问题,我不禁想到,如果有人投资于整个事情实际上代表共同利益来处理基准,那就更好了。提示提示。 :-P
-
如果插入次数很少,而性能确实是问题所在,我建议使用
ConcurrentDictionary<>甚至Dictionary<>以外的其他内容。它们并不完全是缓存友好的。在不可变排序数组行中有一些东西,即完全(原子地)复制+插入替换是更好的选择。 -
我只是想基本上说一下@nothrow 刚才所说的内容,并补充说
ImmutableDictionary是另一种选择(如果您想按类型索引,比自己维护数组稍微简单一些)。即使如此,通过一个小数组进行线性搜索也可以比字典查找更快甚至更快,具体取决于具体情况(但无论如何使用Dictionary-like 类型来涵盖您 do的情况> 如果不能保证,拥有超过 X 个元素比依赖过早优化要好。) -
您是否进行了任何内存分析以查看设置容量 (1-15) 的实际效果?
标签: c# primes concurrentdictionary capacity