unordered_set (C++) 在字符串的情况下与在整数的情况下的性能有啥不同吗?
Posted
技术标签:
【中文标题】unordered_set (C++) 在字符串的情况下与在整数的情况下的性能有啥不同吗?【英文标题】:Is there any difference in perofrmance of unordered_set (C++) in case of strings vs in case of integer?unordered_set (C++) 在字符串的情况下与在整数的情况下的性能有什么不同吗? 【发布时间】:2021-06-16 09:24:03 【问题描述】:我想知道 unordered_set 使用散列,所以在整数的情况下应该比在字符串的情况下更快。 unordered_map 也是如此。我在网上没有找到明确的答案。如果有人能澄清这一点,那就太好了。
【问题讨论】:
您要对std::hash<std::string>
和 std::hash<int>
进行基准测试吗?
确切的性能在很大程度上取决于特定的哈希实现和数据输入,您需要进行分析。也就是说,我当然希望散列整数不会比散列字符串慢,但我可以想象它们大致相等的情况。如果您的字符串都很短,因此直接存储在 std::string
对象中,并进行了短字符串优化,没有间接性,那么散列直接存储在键中的几个字节就不会真正关心类型系统所说的内容那些字节。
嗯,散列本身的计算往往与被散列的数据大小成正比,但是如果你有什么,很难看出散列整数比散列字符串更快有什么帮助字符串。
我想散列 64 个字符串而不是散列一个整数(4 个字节)。就我而言,我认为大小有很大差异。对于短字符串,毫无疑问不会有任何明显的区别
可能有点 OT:我记得育碧这个家伙的谈话,他说他们不发布带有以字符串为键的地图的游戏,here,他还说他们使用自定义容器,所以它可能不适用于 STL 容器。但也许他所说的与你所问的有关。我很想知道真相在哪里! :)
【参考方案1】:
unordered_set (C++) 在字符串和整数的情况下的性能有什么不同吗?
可以的。语言规范并不能保证任何一种方式。
您可以通过测量性能来验证您的程序在目标系统上是否属于这种情况。
如果您正在考虑是使用字符串本身作为键,还是使用单独散列的字符串(即整数),那么技术上单独的散列会更昂贵,因为整数会再次被散列。也就是说,散列一个整数是微不足道的(我认为它可能是恒等函数),所以这可能没有明显的效果。
单独散列 + 存储整数确实具有潜在优势:您可以对字符串键进行一次预散列,并重复使用散列后的整数键,而具有字符串键的映射需要在每次查找时重新散列键。这对您的情况是否有用取决于您要对地图执行的操作。
【讨论】:
【参考方案2】:抽象的答案是“取决于实现和其他细节”,例如键和容器的大小。标准没有指定字符串与整数之间的任何内容,因此您不应期望存在全局有效的答案。
在 x86 / x86_64 上的 gcc / clang 等流行平台上,您的猜测似乎是正确的。在用整数或指针替换映射中的字符串键后,我有获得基本性能胜利的经验。
不过,在某些特定情况下,即使在上述平台上,字符串也会击败整数。
【讨论】:
请注意,map
中的字符串与整数可能会更慢,因为字符串通常比整数更昂贵,而 OP 专门要求哈希
@463035818_is_not_a_number 这就是我要说的:在流行的平台上,字符串散列通常比整数散列慢。
您是在说“在用整数或指针替换映射中的字符串键后,我有获得基本性能胜利的经验。”这可能是由于重新分配字符串或创建字符串比创建 int 更昂贵。
@463035818_is_not_a_number 好吧,正确的。仍然期望在散列方面获胜也是有道理的,因为整数的散列是它的值,而对于字符串,它以更复杂的方式定义。
我的意见是:除非你衡量它,否则期望任何关于性能的东西是没有意义的;)。没关系,可能你是对的【参考方案3】:
C++ 标准未指定散列函数。
也就是说,GCC、Clang 和 Visual C++ 都使用整数的标识哈希 - 这意味着专用于整数类型的 std::hash<>
返回其输入。 Visual C++ 使用 2 的幂次方计数,而 GCC 使用素数。因此,某些输入在 Visual C++ 上非常容易发生冲突,例如指向具有 N 字节对齐的对象的指针 - 其中 N 较大 - 已转换为数字的对象将全部在桶 0、N、2N、3N 等处发生冲突,其中所有桶之间不存储任何数据。另一方面,如果整数足够随机以至于它们恰好分布在桶中而没有过多的冲突(这更可能与 GCC 的主要桶计数有关),那么身份哈希可以节省 CPU 尝试进一步处理它们的时间。
GCC 对字符串使用 MURMUR32 散列,而 Visual C++ 对沿字符串大致均匀采样的 10 个字符进行一些简单的异或移位(因此,GCC 速度较慢但散列质量大大提高,特别是对于相同长度的目录/文件名带有公共前缀的路径,最后只有一个递增的代码,其中 Visual C++ 只能将一个不同的字符合并到散列中)。与将文本存储在自身内部的字符串(一种称为短字符串优化或 SSO 的技术)或整数相比,任何在动态分配的内存(“堆”)中存储较长文本的字符串都将依赖于至少一个额外的至少一个额外的缓存到达文本的行(在现代 x86 架构上,在散列期间访问的字符串的每个 64 字节块可能需要额外的缓存错误)。
可以创建一个对象来存储字符串和哈希 - 计算一次 - 但这并不是问题所要问的,在找到哈希值匹配后,您仍然需要比较整个字符串内容确定匹配。
总之,如果您在 Visual C++ 上使用带有冲突倾向键的默认标识哈希,它可能比使用字符串慢(如果字符串哈希具有较少的冲突,这远非确定)。但是,在大多数情况下,使用整数键会更快。
如果您真的关心,请始终在您自己的系统和数据集上进行基准测试。
【讨论】:
以上是关于unordered_set (C++) 在字符串的情况下与在整数的情况下的性能有啥不同吗?的主要内容,如果未能解决你的问题,请参考以下文章
在 c++ 中使用 unordered_set/map 时如何制作封装良好的类?
C++之unordered_map和unordered_set以及哈希详解
C++之unordered_map和unordered_set以及哈希详解
C++中map/set和unordered_map/unordered_set的区别及其适用情况