为什么复杂循环和多个简单循环之间存在如此巨大的性能差异?

3
我正在为一个项目进行快速循环,我决定组合一个简单的测试来查看性能在比较运行更复杂的内部循环和运行几个简单的内部循环时如何变化。这是jsperf的测试链接:http://jsperf.com/nested-fors-vs-iterative-fors。我惊讶于差异的显著性。有人可以指出我的测试有问题吗(这很可能是情况),或者解释为什么存在性能差异吗?谢谢!

我在你提供的页面中遇到了“ReferenceError: _未定义”的错误。 - hugomg
3个回答

3
我不知道为什么你需要underscore.js和jQuery。我已经写了一个非库版本进行比较:(代码链接) 比较的内容如下:
  1. 100个循环每次调用10次函数,共1,000次。
  2. 100个调用每个调用一个函数,共1,000次。
  3. 200个调用每个调用5个函数,共1.000次。
  4. 1,000个循环每次调用1次函数,共1,000次。
它们之间的性能差异相当小。
我认为你的问题是第一种情况是100个匿名函数每个调用10次函数。而第二种情况是1,000个匿名函数每个调用1次函数。开销可能在于创建匿名函数(虽然我没有查看使用的库的相关内部部分)。

谢谢RobG - 我现在明白了我的测试问题所在!我只使用underscore.js,因为它是我常用的库,我很熟悉它。我不认为它的迭代器会那么重,但我也不太确定。 - idbentley

1

我认为你的测试存在着不可比较的问题 - 在第一个测试中,你循环遍历了整数数组1 * 100共100次,总共执行了你的函数1000次(每个函数执行10次)。

而在第二个测试中,你循环遍历了整数数组10 * 100共1000次,每次调用了10个匿名函数,每个函数都调用了你的doSomething函数一次。遍历一个100项的数组10次所需的时间比遍历一次要长。创建10个匿名函数并且每个函数都调用100次肯定比创建一个匿名函数并且调用它100次要多出很多开销。这两个因素使得你的测试完全不同。

请尝试使用以下方法:

function doSomething(a) {
  a * 10 + a * 1000000.0 / 50.0;
}
var range = _.range(100),
    total_runs = _.range(10);
_.each(total_runs, function(a) {
    _.each(range, doSomething);
});

我认为遍历数组不需要太多时间,它似乎已经高度优化了。很可能是10倍于匿名函数需要额外的工作,因此需要更长时间。 - RobG
@RobG -- 可能吧。我敢打赌这更多是因为 _.each 每次在可选上下文对象的上下文中调用传递给它的函数,并在每次循环中执行一些额外的检查,而嵌套循环这种情况永远不会胜过更紧密优化(即更具体使用案例)的代码。(参见:http://documentcloud.github.com/underscore/docs/underscore.html#section-12)但只有分析器才能给我们一个确定和最终的答案。 :-) - Sean Vieira

1
在第一种情况下,您调用一个迭代器函数100次。在第二种情况下,您调用10个迭代器函数100次。这就是函数调用数量增加了10倍的原因。我认为这就是导致差异的开销。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接