【问题标题】:es6 Map and Set complexity, v8 implementationes6 Map and Set 复杂度,v8 实现
【发布时间】:2016-02-10 05:45:04
【问题描述】:

在 v8 实现中检索/查找是 O(1) 是否合理?

(我知道标准并不能保证)

【问题讨论】:

  • 平均?还是最坏的情况?
  • 标准does guarantee sub-linear complexity,顺便说一句。
  • @Oriol 两者都会很有趣。
  • 不确定 V8 使用什么,但 spec 提到哈希表是一种可能性。 Hash tables 平均有恒定的时间操作,在最坏的情况下是线性的。
  • 可以找到另一个很好的答案here

标签: javascript ecmascript-6 set complexity-theory v8


【解决方案1】:

在 v8 实现中检索/查找是 O(1) 是否合理假设?

是的。 V8 使用哈希表的变体,这些操作的复杂度通常为 O(1)

有关详细信息,您可能想查看https://codereview.chromium.org/220293002/,其中OrderedHashTable 是基于https://wiki.mozilla.org/User:Jorend/Deterministic_hash_tables 实现的。

【讨论】:

  • 为了扩展一点,V8 中的 Map 和 Set 最近在 JavaScript 中重新实现 codereview.chromium.org/947683002 这在 V8 中很常见,在 JavaScript 中实现新功能让 JIT(Crankshaft/Turbofan)优化运行时代码.
  • @DiegoPino:谢谢。我不知何故认为OrderedHashTable 实现仍然是最新的,因为我在trunk 中找到了它……
  • 看起来实现已经从 src/collections.js 移动到 src/builtins/builtins-collections-gen.cc github.com/v8/v8/blob/master/src/builtins/…
【解决方案2】:

对于不想把兔子洞挖得太深的人:

1:我们可以假设良好的哈希表实现实际上具有 O(1) 时间复杂度。
2:这是 V8 团队发布的一篇博客,解释了如何对 MapSetWeakSetWeakMap哈希表实现 进行一些内存优化:Optimizing hash tables: hiding the hash code

基于 1 和 2:V8 的 Set 和 Map 的 get & set & add & has 时间复杂度实际上是 O(1)。

【讨论】:

    【解决方案3】:
    let map = new Map();
    let obj = {};
    
    const benchMarkMapSet = size => {
    console.time("benchMarkMapSet");
    for (let i = 0; i < size; i++) {
        map.set(i, i);
    }
    console.timeEnd("benchMarkMapSet");
    };
    
    const benchMarkMapGet = size => {
    console.time("benchMarkMapGet");
    for (let i = 0; i < size; i++) {
        let x = map.get(i);
    }
    console.timeEnd("benchMarkMapGet");
    };
    
    const benchMarkObjSet = size => {
    console.time("benchMarkObjSet");
    for (let i = 0; i < size; i++) {
        obj[i] = i;
    }
    console.timeEnd("benchMarkObjSet");
    };
    
    const benchMarkObjGet = size => {
    console.time("benchMarkObjGet");
    for (let i = 0; i < size; i++) {
        let x = obj[i];
    }
    console.timeEnd("benchMarkObjGet");
    };
    
    let size = 2e6;
    
    benchMarkMapSet(size);
    benchMarkObjSet(size);
    benchMarkMapGet(size);
    benchMarkObjGet(size);
    

    benchMarkMapSet: 382.935ms benchMarkObjSet: 76.077ms benchMarkMapGet: 125.478ms benchMarkObjGet: 2.764ms

    benchMarkMapSet: 373.172ms benchMarkObjSet: 77.192ms benchMarkMapGet: 123.035ms benchMarkObjGet: 2.638ms

    【讨论】:

    • 那是因为 Map 类是按插入排序的。
    【解决方案4】:

    最初的问题已经得到解答,但 O(1) 并不能说明实现的效率。

    首先,我们需要了解哈希表的哪些变体用于 Maps。 “经典”哈希表不会这样做,因为它们不提供任何迭代顺序保证,而 ES6 规范要求插入以迭代顺序进行。因此,V8 中的地图构建在所谓的deterministic hash tables 之上。这个想法类似于经典算法,但是桶有另一层间接,所有条目都插入并存储在一个固定大小的连续数组中。确定性哈希表算法确实保证了基本操作的 O(1) 时间复杂度,例如 setget

    接下来,我们需要知道哈希表的初始大小、负载因子以及它如何(以及何时)增长/收缩。简短的回答是:初始大小为 4,负载因子为 2,表(即 Map)一旦达到其容量就会增长 x2,一旦有超过 1/2 的删除条目就会缩小。

    让我们考虑最坏的情况,即表有 N 个条目中的 N 个(已满),所有条目都属于一个桶,并且所需的条目位于尾部。在这种情况下,查找需要 N 次遍历链元素。

    另一方面,在表已满但每个存储桶有 2 个条目的最佳情况下,查找最多需要 2 次移动。

    众所周知的事实是,虽然哈希表中的单个操作“便宜”,但重新散列却不是。重新散列具有 O(N) 时间复杂度,并且需要在堆上分配新的散列表。此外,在必要时,重新散列作为插入或删除操作的一部分执行。因此,例如,map.set() 调用可能比您预期的要贵。幸运的是,重新散列是一种相对不常见的操作。

    除此之外,诸如内存布局或哈希函数之类的细节也很重要,但我不打算在这里讨论这些细节。如果您对 V8 Maps 的底层工作方式感到好奇,您可能会发现更多详细信息 here。前段时间我对这个话题很感兴趣,并试图以一种可读的方式分享我的发现。

    【讨论】:

      【解决方案5】:

      我们为什么不直接测试一下。

      var size_1 = 1000,
          size_2 = 1000000,
          map_sm = new Map(Array.from({length: size_1}, (_,i) => [++i,i])),
          map_lg = new Map(Array.from({length: size_2}, (_,i) => [++i,i])),
          i      = size_1,
          j      = size_2,
          s;
      
      s = performance.now();
      while (i) map_sm.get(i--);
      console.log(`Size ${size_1} map returns an item in average ${(performance.now()-s)/size_1}ms`);
      s = performance.now();
      while (j) map_lg.get(j--);
      console.log(`Size ${size_2} map returns an item in average ${(performance.now()-s)/size_2}ms`);

      所以对我来说,随着大小的增长,它似乎收敛到 O(1)。那么我们称之为 O(1)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-12
        • 2015-12-19
        • 2019-06-05
        • 2012-09-28
        • 2012-07-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多