为啥 unordered_multiset 对许多相等的键不起作用
Posted
技术标签:
【中文标题】为啥 unordered_multiset 对许多相等的键不起作用【英文标题】:Why unordered_multiset works bad for many equal keys为什么 unordered_multiset 对许多相等的键不起作用 【发布时间】:2015-10-05 06:57:15 【问题描述】:我有这段代码:
unordered_multiset<int> t;
for (int i = 0; i < 1000000; i++)
if (i % 10000 == 0)
cout << i << endl;
t.insert(10);
所以它只是在unordered_multiset
中放置了许多相同的元素。但是我发现我在哈希中的元素越多,这个工作就越慢?我无法理解原因。在我看来,在应用哈希函数并找到相等元素的桶后(因为所有相等的元素都组合在一起)stl 只需将它们放在桶的末尾即可。
那么这里出了什么问题?
UDP: 我找到了 unordered_multiset::insert 函数的描述
单元素插入: 平均情况:恒定。 最坏的情况:容器大小呈线性关系。
所以现在的问题可以改写为:“为什么最坏的情况是线性的”
【问题讨论】:
我们在散列中拥有的元素越多,运行速度就越慢?那么,我们在这里谈论的是什么操作?查找还是插入?? 找到存储桶的时间是固定的摊销时间(即O(1)
)。但是桶 afaik 是一个链表。
“所以插入离子应该很快,不是吗?”不必要。现代 2-3 级高速缓存内存架构违背理论。链表本质上是一种非缓存友好的数据结构。
很抱歉,我不能认为这样的测量是精确的。使用计时器测量并显示同一项目的平均插入时间对于 100、1000、10000、100000、1000000 次插入有显着差异,那么这将是一个有趣的点。
对于 GCC 4.7.2 在我的机器上插入 100 万个元素需要 0.347 秒,1000 万需要 3.4 秒,1 亿需要 37.2 秒 - 对我来说看起来很线性。您使用 Nikita 的编译器/版本是什么?
【参考方案1】:
一切都在同一个桶中。要把东西放在桶的末端,你必须找到桶的末端,桶里的东西越多,需要的时间就越长。
【讨论】:
为什么桶不存储指向最后一个列表节点的指针以便更快地插入? 为什么要这样?性能限制不需要它,允许插入花费线性时间。并且它会在非常常见的操作上产生成本,例如插入具有唯一键的值。每个实例都会支付这样一个指针的成本,只有插入大量相等键的实例才会受益。这些情况可以自己优化操作,例如,通过使用映射到值列表。 是的,我的问题间接针对性能限制。也就是说,我找不到 为什么 允许插入需要线性时间的理由。我同意开销可能是原因;但是使用映射会使列表(或间接)变得不必要。 @dyp 很可能是因为所有值的哈希值都相同但不相等。在这种情况下,除了将新插入的元素与之前的每个元素进行比较之外,插入还能做什么? @dyp 首先要知道该条目是否与桶中的其他条目具有相同的值。否则无法满足等值键排序在一起的排序约束。【参考方案2】:容器尝试通过重新组织存储来平衡自身,以便平均存储桶大小低于load_factor。它通过添加更多桶来实现这一点,希望数据分布更均匀。
当您在所有元素中存储相同的值时,无论如何它们最终都会存储在同一个存储桶中。哈希表的最坏情况!
【讨论】:
以上是关于为啥 unordered_multiset 对许多相等的键不起作用的主要内容,如果未能解决你的问题,请参考以下文章
22.允许重复的容器(unordered_multiset)
为啥对 XML 文件的 Linq 查询只检查许多参数的第一个参数?