代码之家  ›  专栏  ›  技术社区  ›  KooiInc

为什么array.push有时比array[n]=value更快?

  •  77
  • KooiInc  · 技术社区  · 17 年前

    作为测试一些代码的附带结果,我编写了一个小函数来比较使用 array.push(value) 方法与直接寻址 array[n] = value

    const arrLen = 10_000;
    const x = [...Array(10)].map( (_, i) => testArr(arrLen, i));
    console.log(`Array length: ${arrLen}\n--------\n${x.join(`\n`)}`);
    
    function testArr(n, action) {
      let arr = [];
      const perfStart = performance.now();
      const methods = 
        ` for (; n; n--) arr.push(n)
          for (; i < n; i += 1) { arr[i] = i; }
          for (; i < n; i += 1) arr.push(i)
          while (--n) arr.push(n)
          while (i++ < n) arr.push(n)
          while (--n) arr.splice(0, 0, n)
          while (--n) arr.unshift(n)
          while (++i < n) arr.unshift(i)
          while (--n) arr.splice(n - 1, 0, n)
          while (n--) arr[n] = n`.split(`\n`).map(v => v.trim());
      const report =  i => `${methods[i]}: ${
        (performance.now() - perfStart).toFixed(2)} milliseconds`;
      let i = 0;
      
      switch (action) {
        case 0: for (; n; n--) arr.push(n)
        case 1: for (; i < n; i += 1) { arr[i] = i; } break;
        case 2: for (let i = 0; i < n; i += 1) arr.push(i); break;
        case 3: while (--n) arr.push(n); break;
        case 4: while (i++ < n) arr.push(n); break;
        case 5: while (--n) arr.splice(0, 0, n); break;
        case 6: while (--n) arr.unshift(n)
        case 7: while (++i < n) arr.unshift(i); break;
        case 8: while (--n) arr.splice(n - 1, 0, n); break;
        default: while (n--) arr[n] = n;
      }
      
      return report(action);
    }
    .as-console-wrapper {
        max-height: 100% !important;
    }
    5 回复  |  直到 3 年前
        1
  •  89
  •   Bharat Khatri olliej    12 年前

    在您的情况下,您首先设置最后一个元素,这意味着JS引擎将看到一个需要长度为 n

    为了回应@Christoph并出于拖延的愿望,这里描述了如何在JS中(通常)实现数组——具体细节因JS引擎而异,但一般原理是相同的。

    所有JS Object s(所以不是字符串、数字、真、假, undefined null

    interface Object {
        put(propertyName, value)
        get(propertyName)
    private:
        map properties; // a map (tree, hash table, whatever) from propertyName to value
    }
    

    此Object类型处理所有标准属性访问逻辑、原型链等。

    interface Array : Object {
        override put(propertyName, value)
        override get(propertyName)
    private:
        map sparseStorage; // a map between integer indices and values
        value[] flatStorage; // basically a native array of values with a 1:1
                             // correspondance between JS index and storage index
        value length; // The `length` of the js array
    }
    

    现在,当你在JS中创建一个数组时,引擎会创建类似于上述数据结构的东西。当你在Array实例中插入一个对象时,Array的put方法会检查属性名是否是0到2^32-1(或者可能是2^31-1,我完全忘记了)之间的整数(或者可以转换为整数,例如“121”、“2341”等)。如果不是,则put方法被转发到基本Object实现,并完成标准[[put]]逻辑。否则,该值将被放置在Array自己的存储中,如果数据足够紧凑,则引擎将使用平面数组存储,在这种情况下,插入(和检索)只是一个标准的数组索引操作,否则引擎将把数组转换为稀疏存储,并使用映射从propertyName获取值位置。

    propertyName 由于字符串JS引擎也倾向于专注于整数查找,因此 someObject[someInteger] NodeList

        2
  •  11
  •   devios1    14 年前

    • (降序)1000000个值:3.697

    在IE7上:

    • 数组[n..0]=值(降序)1000000个值:1.141秒

    你的测试 该方法在IE7上似乎更好(差异巨大),由于在其他浏览器上差异很小,因此似乎是 方法确实是向数组添加元素的最佳方式。

    simple test script ,所以我真的不知道该说什么或想什么了,我无能为力。

    顺便说一句:在我的IE7上,你的脚本停止了,浏览器问我是否想让它继续运行(你知道典型的IE消息是:“停止运行此脚本?…”)

        3
  •  7
  •   Christoph    17 年前

    push()

    []


        4
  •  6
  •   Timo Kähkönen    12 年前

    array[n] = value new Array(n) array.push n >= 90 .

    your page Array[0 .. n] = value (ascending)

    Array.push(n) 有时在第一次运行中会领先,但在随后的测试运行中

    var array = new Array(n) Array.push(n)

    https://jsperf.com/push-method-vs-setting-via-key/10

    function testArr(n, doPush){
    
      var now = new Date().getTime(),
                      duration,
                      report =  ['<b>.push(n)</b>',
                                 '<b>.splice(0,0,n)</b>',
                                 '<b>.splice(n-1,0,n)</b>',
                                 '<b>[0 .. n] = value</b> (ascending)',
                                 '<b>[n .. 0] = value</b> (descending)'];
      doPush = doPush || 5;
    
      if (doPush === 1) {
       var arr = [];
       while (--n) {
         arr.push(n);
       }
      } else if (doPush === 2) {
       var arr = [];
       while (--n) {
        arr.splice(0,0,n);
       }
      } else if (doPush === 3) {
       var arr = [];
       while (--n) {
        arr.splice(n-1,0,n);
       }
      } else if (doPush === 4) {
       var arr = new Array(n);
       for (var i = 0;i<n;i++) {
        arr[i] = i;
       }
      } else {
        while (--n) {
        var arr = [];
          arr[n] = n;
        }
      }
      /*console.log(report[doPush-1] + '...'+ arr.length || 'nopes');*/
      duration = ((new Date().getTime() - now)/1000);
      $('zebradinges').innerHTML +=  '<br>Array'+report[doPush-1]+' 1.000.000 values: '+duration+' sec' ;
      arr = null;
    }
    
        5
  •  0
  •   Magne    6 年前

        6
  •  -6
  •   Stiropor    17 年前