有an established guideline获取哈希码不应该分配内存,因为这会通过调用垃圾收集器对哈希表查找产生负面影响。
然而,这确实是失败的是我看到我使用System.Collections.Generic.Dictionary我的应用程序的概况
在非常紧凑的循环中,我在我的探查器结果中找到以下内容:
这是分析器的整个子树会计。
我不是这项特殊工作的经验丰富的专家,所以我可能会错误地阅读这些茶叶。但它看起来像GetHashCodeOfString“必须”分配内存并邀请垃圾收集器在这个循环的中间中断我的程序我想要真正的调整和紧,这是占这个循环的惊人的大部分成本。
除此之外,here is an additional piece of evidence suggesting this code allocates memory
我的下一步是使用序数比较器初始化Dictionary并重新运行我的测试。
但我想知道围绕这个问题是否存在现有的智慧。看起来像带有字符串键的字典很常见,并且可以很好地探索这种常见事物的成本。我发现了以下分析,但它将重点放在实际比较上作为祸患的原因,而不是分配内存的哈希码方法。
有人能建议使用带有字符串键的字典的正确方法来避免这个问题吗?
我的具体问题包括:
更新:使用序数比较器(仍然使用IgnoreCase)进行另一个测试,并将原始结果输出重新设置为100%cost = TryGetValue,这样它就会更多苹果到苹果
原件:
序:
TryGetValue的总体时间花费似乎也有显着下降。我不小心确保所有其他方面都相同,但在第一次运行的10分钟压力测试中这占了46秒,而在orindal运行中它占了252毫秒。考虑一下轶事,而不是预期的相对成本。
似乎哈希的全部成本(过去是成本的99 +%)现在是如此“免费”,甚至无法出现在探查器中,我认为它在采样模式下运行。 / p>
我猜这个街道上的这个词你应该使用顺序比较。
我仍然无法向自己说明为什么GC成本对第一个配置文件结果的贡献如此之大,但是从下面的评论中我想我必须相信它不会分配托管堆内存,但是因为它很慢,它往往是由其他线程上的其他活动“随机”GC的函数,因为此进程确实使用服务器模式gc。
也许这表明这个紧密循环往往与其他地方的分配快乐代码并发。
答案 0 :(得分:10)
默认情况下,当您使用string
个密钥时,会使用string.GetHashCode()
。这个方法不会在堆上分配任何内存,而且应该非常快。
但是由于您使用了忽略大小写,因此使用了CultureAwareComparer.GetHashCode()
。该方法调用(从您的个人资料结果中可以看到)CompareInfo.GetHashCodeOfString()
,后者又调用非托管函数InternalGetGlobalizedHashCode()
。两个托管方法都没有进行任何堆分配(如果你在反编译器中查看它们就可以看到)。我不能说InternalGetGlobalizedHashCode()
做了什么,但由于它是不受管理的,我怀疑它是否在托管堆上进行任何分配。无论如何,它必须比默认的哈希代码计算复杂得多,特别是因为它具有文化意识,必须记住像Turkish İ这样的问题。
这意味着您可能有一些其他代码在堆上分配内存,这会导致垃圾回收。
如果你想要获得最佳表现,你应该避免“忽视案例”,特别是它的文化意识变种。