Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

哈希

HashSetHashMap 是两种广泛使用的类型,有一些方法可以让它们更快。

替代哈希器

默认的哈希算法未指定,但在撰写本文时,默认算法是一种名为 SipHash 1-3 的算法。 这种算法质量很高——提供了很高的碰撞防护能力——但相对较慢,特别是对于整数这样的短键。

如果性能分析表明哈希是热点,且 HashDoS 攻击对你的应用不是问题,那么使用具有更快 哈希算法的哈希表可以带来巨大的速度提升。

  • rustc-hash 提供了 FxHashSetFxHashMap 类型,可作为 HashSetHashMap 的直接替代品。它的哈希算法质量较低但速度非常快,尤其适用于整数键, 并且已被发现在 rustc 中的性能优于所有其他哈希算法。(fxhash 是相同算法和类型的 较旧、维护较少的实现。)
  • fnv 提供了 FnvHashSetFnvHashMap 类型。它的哈希算法质量比 rustc-hash 更高,但稍慢一些。
  • ahash 提供了 AHashSetAHashMap。它的哈希算法可以利用某些处理器上可用的 AES 指令支持。

如果哈希性能在程序中很重要,值得尝试多种替代方案。例如,在 rustc 中观察到了以下结果。

如果你决定普遍使用某种替代方案,例如 FxHashSet/FxHashMap,很容易在某些地方 意外地使用 HashSet/HashMap。你可以使用 Clippy 来避免这个问题。

有些类型不需要哈希。例如,你可能有一个包装整数的 newtype,且整数值是随机的或接近随机。 对于此类类型,哈希值的分布与值本身的分布不会有太大差异。在这种情况下, nohash_hasher crate 可能会很有用。

哈希函数设计是一个复杂的主题,已超出本书的范围。ahash 文档中有很好的讨论。

逐字节哈希

当你用 #[derive(Hash)] 注解一个类型时,生成的 hash 方法将分别哈希每个字段。 对于某些哈希函数,将类型转换为原始字节并将字节作为流进行哈希可能会更快。这对于满足 某些属性(例如没有填充字节)的类型是可行的。

zerocopybytemuck crate 都提供了 #[derive(ByteHash)] 宏,用于生成 执行这种逐字节哈希的 hash 方法。derive_hash_fast crate 的 README 提供了 关于此技术的更多细节。

这是一项高级技术,性能效果高度依赖于哈希函数和被哈希类型的具体结构。请仔细测量。