【问题标题】:Linked List vs Open Addressing in Hash Table链表与哈希表中的开放寻址
【发布时间】:2017-12-01 07:43:17
【问题描述】:

我有一个包含 130000 个元素的数据集,我有两个不同的数据结构,即双向链表和哈希表。在将数据集元素插入到链表中时,我使用尾指针将节点放在链表的末尾。在将数据集元素插入哈希表时,我受益于带有探测功能的开放寻址方法。对于数据集中的最后 10 个元素,我面临 110000 次碰撞。

但是,两个不同数据结构的插入总运行时间之差等于 0.0981 秒。

链表 = 0.028521 秒

哈希表 = 0.120102 秒

是指针操作慢还是探测方法很快?

【问题讨论】:

  • 如果您有 10 个元素的 110000 次碰撞,那么就有问题了。哈希表可以达到多满是有限制的……您的哈希表有多大?你怎么试探?但问题也可能是哈希函数(不太可能)。
  • 就个人而言,在大多数情况下最适合我的是使用单链表的缓存友好的单独链接实现。 “缓存友好”和“链表”可能看起来有些矛盾,但您可以分配列表节点以使它们通常是连续的。例如,链表可以用 next 列表指针来实现,它只是一个数组的 32 位索引(实际上我就是这样做的)。如果您有空间进行后期处理,您甚至可以重建哈希表,以确保存储桶中的每个相邻节点都是连续的。
  • 理论上不一定是最优的,但我发现它是实现高性能解决方案的最简单方法,无需过多考虑。哈希表本身也只是一个桶头索引数组,它用next 索引索引一个节点。列表节点存储在一个巨大的数组中(std::vector,即)。我喜欢这个解决方案的原因是内存使用是非常可预测的。每个桶 4 个字节,每个插入的节点 4 个字节。

标签: c++ performance list data-structures hash


【解决方案1】:

通过尾指针在双链表末尾插入是 O(1),因为您也阅读了 here

在具有开放寻址的哈希表中插入也可以是常量,因为您也可以阅读 here

但是,正确且有效地实现具有开放寻址的哈希表是相当棘手的,因为很多事情都可能出错(探测、负载因子、哈希函数等)。甚至Wikipedia 也提到了这一点..

对于最后 10 个元素,我面临 110000 次冲突(在哈希表中)

这强烈表明您的哈希表实现存在问题。

这解释了为什么您所做的时间测量(如果它们是正确的)会使双链表比您的哈希表更快。

【讨论】:

    【解决方案2】:

    是指针操作慢还是探测方法很快?

    没有实际的算法,所以答案将是理论上的。

    就性能而言,一般的答案是:缓存未命中代价高昂。 DDR 具有 60 ns 延迟和 3.2 GHz CPU,最后一级缓存未命中会使 CPU 停滞 60 * 3.2 =~ 200 个周期。

    双向链表

    对于双链表算法,你必须访问尾指针、尾元素和新元素。如果你只是在循环中添加元素,那么所有这些访问很可能都在 CPU 缓存中。

    在现实生活中的应用程序中,如果你在添加之间做一些事情,你可能会有多达 3 次缓存未命中(尾指针、尾元素、新元素)。

    哈希表

    对于具有开放寻址的哈希表,情况有点不同。散列函数在散列表中产生一个随机索引,所以通常,对散列表的第一次访问是缓存未命中。在您的情况下,哈希表不是那么大(130K 指针),因此它可能适合 L3 缓存。但是,L3 缓存未命中仍然是大约 30 个周期的 CPU 停顿。

    但是接下来会发生什么?您只需将指针放入表中,无需更新尾元素或新元素。所以这里没有缓存未命中。

    如果哈希表元素被占用,则只需检查下一个。这种顺序访问很容易被 CPU 预取器预测,因此所有这些访问通常也不会产生任何缓存未命中:CPU 将下一个哈希表预取到 L1 缓存中。

    所以,在实际应用中,hash table 通常会有一个 cache miss,但是由于 hash 是不可预测的,hash table 总是有这个第一个 cache miss。

    答案

    要获得实用的答案,即您的应用程序中发生了什么,您应该使用一种工具来分析 CPU 性能计数器,例如 Linux 上的 perf 或 Windows 上的 VTune。该工具将显示您的 CPU 究竟花在了哪些方面。

    实际应用

    这里还有一个理论上的免责声明。我想,如果你修复你的哈希表(比如,每个桶使用几个元素而不是开放寻址)并有效地减少冲突的数量,性能可能会相当。

    您应该在哈希表上使用双链表还是反之亦然?这取决于您的应用程序。哈希表适用于随机访问,即您可以在 O(1) 时间内访问任何元素。对于双链表,您必须遍历列表,因此估计为 O(n)。

    另一方面,仅在列表末尾添加元素不仅成本更低,而且实现起来也容易得多。您不关心任何冲突和哈希表溢出。

    因此,在某些情况下,双链表比哈希表具有巨大的优势,这取决于应用程序什么最适合。

    【讨论】:

      猜你喜欢
      • 2011-02-03
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      • 2017-05-11
      • 1970-01-01
      • 2016-01-29
      • 2011-11-17
      • 2012-02-25
      相关资源
      最近更新 更多