为什么流行音乐比移动快?

时间:2011-06-28 03:13:01

标签: javascript performance

道格拉斯·克罗克福德(Douglas Crockford)在 JavaScript:The Good Parts 中指出,“转变通常比流行音乐慢得多”。 jsPerf confirms this。有谁知道为什么会这样?从一个不成熟的角度来看,他们似乎做了几乎相同的事情。

5 个答案:

答案 0 :(得分:30)

要删除返回的项而不重新寻址数组并使对它的所有引用无效,shift()需要移动整个数组; pop()可以简单地从长度中减去1。

答案 1 :(得分:18)

shift()必须重新索引整个数组,而pop()则没有。

pop()只是删除数组中的最后一个元素。因此,元素不动;只需要更新.length

shift()删除数组中的第一个元素。这需要重新索引数组中的所有元素,以便[1]变为[0],依此类推。

答案 2 :(得分:9)

我正在对节点(使用chrome v8)进行一些测试,并注意到对于大约120k元素的阵列,shift的性能非常接近pop。一旦你超过120K,它似乎会大幅放缓。

var sum;
var tests = [125000,130000];

console.log(JSON.stringify(process.versions));

tests.forEach(function(count) {
    console.log('Testing arrays of size ' + count);
    var s1 = Date.now();
    var sArray = new Array(count);
    var pArray = new Array(count);
    for (var i = 0; i < count ; i++) {
      var num = Math.floor(Math.random() * 6) + 1
      sArray[i] = num;
      pArray[i] = num;
    }
    console.log(' -> ' + (Date.now() - s1) + 'ms: built arrays with ' + count + ' random elements');

    s1 = Date.now();
    sum = 0;
    while (pArray.length) {
      sum += pArray.pop();
    }
    console.log(' -> ' + (Date.now() - s1) + 'ms: sum with pop() ' + count + ' elements, sum = ' + sum);

    s1 = Date.now();
    sum = 0;
    while (sArray.length) {
      sum += sArray.shift();
    }
    console.log(' -> ' + (Date.now() - s1) + 'ms: sum with shift() ' + count + ' elements, sum = ' + sum);
});

输出:

{"http_parser":"1.0","node":"0.10.22","v8":"3.14.5.9","ares":"1.9.0-DEV","uv":"0.10.19","zlib":"1.2.3","modules":"11","openssl":"1.0.1e"} 
Testing arrays of size 125000
-> 14ms: built arrays with 125000 random elements
-> 2ms: sum with pop() 125000 elements, sum = 436673
-> 6ms: sum with shift() 125000 elements, sum = 436673 
Testing arrays of size 130000
-> 50ms: built arrays with 130000 random elements
-> 1ms: sum with pop() 130000 elements, sum = 455971
-> 54372ms: sum with shift() 130000 elements, sum = 455971

答案 3 :(得分:0)

如果你移动,你可以向后复制数组中的所有元素。要弹出,您只需要减少数组的长度。从技术上讲,一个实现可以解决这个问题,但你需要存储一个额外的`shift'变量来告诉你数组真正的起始位置。但是,这种类型的操作在实践中并没有被证明非常有用,因此大多数实现只通过存储数组指针的开始和长度值来节省空间。

答案 4 :(得分:0)

差异可以忽略不计 - 未经优化的执行者可能比shiftpop,但优化的执行者不会。{/ p>

你可以这样优化:

let WrapArray = _=>{
  //Ensure no other ref to `_`.

  let numlike = _=>isNaN(_)?false:true
  let num = _=>Number(_)
  {
    let shift_q = 0
    return new Proxy(_, {
      get(first_t, k){
        switch(k){
          case 'shift': return (z={})=>(z.r=first_t[0 + shift_q], delete first_t[0 + shift_q++], z.r)
          break; case 'length': return first_t.length - shift_q
          break; default: return first_t[numlike(k)?num(k) +/*todo overflowguard*/shift_q:k]
        }
      },
      set(first_t, k, v){
        switch(k){
          case 'length': first_t.length = v + shift_q
          break; default: first_t[numlike(k)?num(k) +/*todo overflowguard*/shift_q:k] = v
        }
      }, 
      has(first_t, k){
        return (numlike(k)?num(k) +/*todo overflowguard*/shift_q:k) in first_t
      },
      deleteProperty(first_t, k){
        delete first_t[numlike(k)?num(k) +/*todo overflowguard*/shift_q:k];return 543
      },
      apply(first_t, t, s){
        first_t.call(t, s)
      },
      construct(first_t, s, t){
        new first_t(...s)
      },
    })
  }
}
(_=WrapArray(['a','b','c'])).shift()
console.log(_.length/*2*/, _[0]/*b*/, _[1]/*c*/, _[2]/*undefined*/)