我使用kmean算法聚集了大约40000点。在程序的第一个版本中,我写了像这样的欧几里德距离函数
var euclideanDistance = function( p1, p2 ) { // p1.length === p2.length == 3
var sum = 0;
for( var i in p1 ){
sum += Math.pow( p1[i] - p2[i], 2 );
}
return Math.sqrt( sum );
};
整个程序非常慢,平均执行时间为7秒。经过一些分析后,我重写了上面这个函数
var euclideanDistance = function( p1, p2 ) { // p1.length === p2.length == 3
var sum = 0;
for( var i = 0; i < p1.length; i++ ) {
sum += Math.pow( p1[i] - p2[i], 2 );
}
return Math.sqrt( sum );
};
现在节目平均需要400毫秒左右。由于我编写for循环的方式,这是一个巨大的时间差异。我通常不会对数组使用for..in
循环,但出于某种原因,我在编写此函数时使用了它。
有人可以解释为什么这两种风格之间存在巨大的性能差异吗?
答案 0 :(得分:22)
看看每次迭代中发生的事情有何不同:
for( var i = 0; i < p1.length; i++ )
i < p1.length
i
增加一个非常简单快捷。
现在看看每次迭代中发生了什么:
重复
- 设P是[[Enumerable]]属性为true的obj的下一个属性的名称。如果没有这样的财产,请返回(正常,V, 空)。
醇>
它必须在可枚举的对象中找到下一个属性。使用你的数组,你知道这可以通过一个简单的整数增量来实现,其中找到下一个可枚举的算法很可能不那么简单,因为它必须处理任意对象及其原型链键。
答案 1 :(得分:7)
作为旁注,如果缓存p1的长度:
var plen = p1.length;
for( var i = 0; i < plen; i++ )
你的速度会略有提升。
...如果您记住该功能,它会缓存结果,因此如果用户尝试相同的数字,您将看到速度大幅提升。
var eDistance = memoize(euclideanDistance);
function memoize( fn ) {
return function () {
var args = Array.prototype.slice.call(arguments),
hash = "",
i = args.length;
currentArg = null;
while (i--) {
currentArg = args[i];
hash += (currentArg === Object(currentArg)) ?
JSON.stringify(currentArg) : currentArg;
fn.memoize || (fn.memoize = {});
}
return (hash in fn.memoize) ? fn.memoize[hash] :
fn.memoize[hash] = fn.apply(this, args);
};
}
eDistance([1,2,3],[1,2,3]);
eDistance([1,2,3],[1,2,3]); //Returns cached value
信用:http://addyosmani.com/blog/faster-javascript-memoization/
答案 2 :(得分:4)
首先,对于for / in和数组,您应该知道this。如果你知道你在做什么,没什么大不了的。
我运行一些非常简单的测试来显示不同循环之间的性能差异: http://jsben.ch/#/BQhED
这就是为什么更喜欢为数组使用经典for循环。
答案 3 :(得分:0)
For / In循环,只是循环遍历对象中的所有属性。由于您没有指定循环需要采用的迭代次数,因此它只是“猜测”它,并继续直到没有更多对象。
使用第二个循环,您指定所有可能的变量... a)起点,b)循环停止前应采取的迭代次数,c)增加起点的计数。
你可以这样思考...... For / In =猜测迭代次数,For(a,b,c)你指定