我为了随后丢弃而白白分配了 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.OrdinalStringComparer.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