为什么C#中大小写不敏感的HashSet<string>性能表现这么差?
为什么不区分大小写的HashSet性能差这么多
核心原因
比较逻辑的开销差距是根本原因
默认HashSet<string>采用序号比较规则,直接对比字符串的UTF-16原始编码,不需要处理任何文化相关的转换规则,.NET运行时对这套逻辑做了极致优化,甚至可以用SIMD指令加速,不管是算哈希值还是判断相等速度都非常快。
而StringComparer.InvariantCultureIgnoreCase是文化相关的比较器,每次计算哈希、判断两个字符串是否相等时,都要先处理全局文化的大小写映射规则,还要兼容不同语言的特殊字符转换逻辑,单步操作的开销比序号比较高几十倍,累计起来就出现了20倍的性能差距。你选用的比较器本身不是性能最优的大小写不敏感实现
如果你的业务只需要处理ASCII字符的大小写不敏感匹配,换成StringComparer.OrdinalIgnoreCase初始化HashSet,性能会直接提升数倍。这个比较器不需要处理复杂的文化规则,仅做ASCII范围内的大小写转换,开销低很多。测试代码的实现逻辑也放大了性能差距
你在普通HashSet的测试里用了「存之前统一转小写,查之前先把待查字符串转小写」的逻辑,ToLowerInvariant()针对ASCII字符串的开销远低于文化相关比较的开销,所以整体性能反而更高。
优化方案
- 性能要求高的场景优先用你现有
Perf_HashSet的实现:存入集合前统一把字符串转成小写/大写,查找前也把待查字符串转成相同大小写,用默认的HashSet做匹配,这是目前性能最高的大小写不敏感查找方案。 - 如果不想手动处理大小写转换,优先用
StringComparer.OrdinalIgnoreCase初始化HashSet,不要用文化相关的比较器。
测试原始数据:
测试场景:Contains()方法性能,100万次迭代,集合大小25,未命中查找
Test Duration (ms) Perf_HashSet 43 Perf_Dictionary 49 Perf_HybridDictionary 63 Perf_ListDictionary 223 Perf_List 225 Perf_HashSet_CaseInsensitive 903
内容的提问来源于stack exchange,提问作者hIpPy
相关产品推荐
相关产品推荐

