繁体   English   中英

为什么lodash.each比原生forEach更快?

[英]Why is lodash.each faster than native forEach?

我试图找到运行具有自己范围的for循环的最快方法。 我比较的三种方法是:

var a = "t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t".split();

// lodash .each -> 1,294,971 ops/sec
lodash.each(a, function(item) { cb(item); });

// native .forEach -> 398,167 ops/sec
a.forEach(function(item) { cb(item); });

// native for -> 1,140,382 ops/sec
var lambda = function(item) { cb(item); };
for (var ix = 0, len = a.length; ix < len; ix++) {
  lambda(a[ix]);
}

这是在OS X上的Chrome 29上。您可以在此处自行运行测试:

http://jsben.ch/BQhED

Lodash的.each几乎是原生.forEach 而且,它是如何比平原快for 巫术? 黑魔法?

_.each()[].forEach()不完全兼容。 请参阅以下示例:

var a = ['a0'];
a[3] = 'a3';
_.each(a, console.log); // runs 4 times
a.forEach(console.log); // runs twice -- that's just how [].forEach() is specified

http://jsfiddle.net/BhrT3/

所以lodash的实现缺少if (... in ...)检查,这可能解释了性能差异。


如上面的评论中指出,到本机的差异for原因,主要是在测试中的附加功能查找。 使用此版本可获得更准确的结果:

for (var ix = 0, len = a.length; ix < len; ix++) {
  cb(a[ix]);
}

http://jsperf.com/lo-dash-each-vs-native-foreach/15

http://kitcambridge.be/blog/say-hello-to-lo-dash/

lo-dash开发人员解释(在这里和视频中)本机forEach的相对速度因浏览器而异。 仅仅因为forEach本机并不意味着它比用forwhile构建的简单循环更快。 首先, forEach必须处理更多特殊情况。 其次, forEach使用回调,具有函数调用的(潜在)开销等。

chrome特别是已知的(至少到LO划线开发者)为具有相对慢forEach 因此,对于该浏览器,lo-dash使用它自己的简单while循环来获得速度。 因此,你看到的速度优势(但其他人没有)。

通过巧妙地选择本机方法 - 如果已知在给定环境中快速使用本机实现 - Lo-Dash避免了与本机相关的性能成本和一致性问题。

是的,lodash / underscore每个甚至都没有.forEach相同的语义。 除非引擎能够快速检查没有getter的稀疏数组,否则有一个细微的细节会使函数真正变慢。

这将符合99%规范,并且与Vod相同的速度运行,对于常见情况:

function FastAlmostSpecForEach( fn, ctx ) {
    "use strict";
    if( arguments.length > 1 ) return slowCaseForEach();
    if( typeof this !== "object" ) return slowCaseForEach();
    if( this === null ) throw new Error("this is null or not defined");
    if( typeof fn !== "function" ) throw new Error("is not a function");
    var len = this.length;
    if( ( len >>> 0 ) !== len ) return slowCaseForEach();


    for( var i = 0; i < len; ++i ) {
        var item = this[i];
        //Semantics are not exactly the same,
        //Fully spec compliant will not invoke getters
       //but this will.. however that is an insane edge case
        if( item === void 0 && !(i in this) ) {
            continue;
        }
        fn( item, i, this );
    }
}

Array.prototype.fastSpecForEach = FastAlmostSpecForEach;

通过首先检查未定义,我们根本不会惩罚循环中的正常数组。 引擎可以使用其内部来检测奇怪的数组,但V8不能。

这是一个更新的链接(大约2015年),显示了比较所有三个的性能差异, for(...)Array.forEach_.eachhttps_.each

注意:放在这里,因为我没有足够的分数来评论接受的答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM