我为了随后丢弃而白白分配了 7.8 MB 的键
分析器(Profiler)揭示了我代码中隐藏的成本。
我的请求处理器从一个巨大的字符串中读取 token 并累加它们的权重。逻辑没问题,但内存出了问题。
在一个热点循环(hot loop)中,我分配了 7.8 MB 的字符串——每次字典查找都会创建一个新键——然后立即将其丢弃。
罪魁祸首是 Substring。每一次切片都会创建一个全新的字符串对象。
200,000 个 token
- 方法 A (Substring):23.4 ms,分配了 7,812 KB。
- 方法 B (Span Lookup):14.9 ms,分配了 0 KB。
方法 B 的运行速度快 1.5 倍,且不产生垃圾(garbage)。
在 .NET 9 中,你可以调用 GetAlternateLookup。你不再需要传递新字符串,而是传递一个 ReadOnlySpan<char>,它只是原始字符串的一个窗口——无需进行复制。
字典直接对该 span 进行哈希处理并与现有键进行比对,在无需分配内存的情况下得出相同的结果。
注意事项
- 支持
StringComparer.Ordinal和StringComparer.OrdinalIgnoreCase。 - 如果你使用的自定义比较器不支持 alternate lookup,则会在运行时抛出异常。
- 非常适合解析器、日志处理器或 CSV 扫描器等热点路径(hot paths)。
- 对于只有少量键的简单查找,可以跳过此方法。
我现在会在代码中专门寻找 Substring 后紧跟 TryGetValue 的模式。这种模式会浪费大量的内存。
Source: https://dev.to/ssukhpinder/78-mb-of-keys-i-allocated-just-to-throw-away-50mn
Optional learning community: https://github.com/ssukhpinder/dev-to-code-samples/tree/main/023-dictionary-alternate-lookup
