unordered_set (C++) 在字符串和整数情况下的性能有什么不同吗?

Is there any difference in perofrmance of unordered_set (C++) in case of strings vs in case of integer?

我想知道 unordered_set 使用散列法,所以在整数的情况下应该比在字符串的情况下更快。 unordered_map 的情况也是如此。我在网上没有找到明确的答案。如果有人能澄清这一点就太好了。

Is there any difference in perofrmance of unordered_set (C++) in case of strings vs in case of integer?

可以的。语言规范没有任何一种方式的保证。

您可以通过测量性能来验证您的程序在目标系统上是否属于这种情况。


如果您正在考虑是使用字符串本身作为键,还是使用单独的散列字符串(即整数),那么从技术上讲,单独的散列会更昂贵,因为整数会再次散列。也就是说,散列一个整数是微不足道的(我认为它可能是恒等函数),所以这可能没有明显的效果。

分离散列+存储整数确实有潜在优势:您可以对字符串键进行一次预散列,然后重复使用散列后的整数键,而带有字符串键的映射需要在每次查找时重新散列键。这对您的情况是否有用取决于您要对地图执行的操作。

抽象的答案是“取决于实现和其他细节”,例如密钥和容器的大小。标准没有指定任何关于字符串与整数的内容,所以你不应该期望有全局有效的答案。

在 x86 上的 gcc / clang 等流行平台上 / x86_64 您的猜测似乎是正确的。我有在用整数或指针替换映射中的字符串键后获得基本性能胜利的经验。

不过,即使在上述平台上,也可能存在字符串胜过整数的特定情况。

C++ 标准未指定散列函数。

也就是说,GCC、Clang 和 Visual C++ 都对整数使用标识哈希 - 这意味着 std::hash<> 专门用于整数类型 returns 其输入。 Visual C++ 使用 2 的幂数桶计数,而 GCC 使用质数。因此,某些输入在 Visual C++ 上极易发生冲突,例如指向具有 N 字节对齐的对象的指针——其中 N 较大——已转换为数字的指针将在存储桶 0、N、2N、3N 等处发生冲突,而所有存储桶之间的存储桶均不存储任何数据。另一方面,如果整数足够随机以至于它们恰好分布在桶中而没有过多的冲突(这更有可能与 GCC 的素数桶计数),那么身份散列可以节省 CPU 时间来尝试进一步处理它们。

GCC 对字符串使用 MURMUR32 哈希,而 Visual C++ 对沿字符串大致均匀采样的 10 个字符进行一些简单的异或和移位(因此,GCC 速度较慢但哈希质量要好得多,特别是对于相同长度的东西 directory/filename 具有公共前缀和末尾递增代码的路径,其中 Visual C++ 可能仅将一个不同的字符合并到散列中)。与将其文本存储在自身内部的字符串(一种称为短字符串优化或 SSO 的技术)或整数相比,任何在动态分配的内存(“堆”)中存储较长文本的字符串都将依赖于至少一个额外的至少一个额外的缓存到达文本的行(在现代 x86 架构上,在散列期间访问的每个 64 字节字符串块可能需要额外的缓存错误)。

可以创建一个对象来存储一个字符串和一个散列 - 计算一次 - 但这并不是问题所问的,并且在找到散列值匹配后你仍然需要比较整个字符串内容确定匹配。

总而言之,如果您在 Visual C++ 上使用默认标识散列和易发生冲突的键,它可能比使用字符串慢(如果字符串散列的冲突较少,这远非确定)。但是,在大多数情况下,使用整数键会更快。

如果您真的很在意,请始终对您自己的系统和数据集进行基准测试。