为啥 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 对许多相等的键不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为啥运行单线程 Java 程序会导致许多内核处于活动状态?

安装oracle 11g,为啥不需要windows系统安装jre就可以安装?

为啥装饰器有用? [关闭]

为啥我的 char 数组长度不正确?

为啥文件扩展名对编译有影响?

为啥要使用证书对客户端进行身份验证?