为啥向后迭代数组比向前迭代更快

Posted

技术标签:

【中文标题】为啥向后迭代数组比向前迭代更快【英文标题】:Why is iterating through an array backwards faster than forwards为什么向后迭代数组比向前迭代更快 【发布时间】:2012-01-31 03:40:30 【问题描述】:

鉴于此代码:

var arr = [];

for (var i = 0; i < 10000; ++i)
    arr.push(1);

转发

for (var i = 0; i < arr.length; ++i) 

向后

for (var i = arr.length - 1; i >= 0; --i) 

硬编码转发

for (var i = 0; i < 10000; ++i) 

为什么倒退这么快?

这是测试: http://jsperf.com/array-iteration-direction

【问题讨论】:

请注意,您的后向案例应从 arr.length-1 开始,并使用 i &gt;= 0 而不是 i &gt; 0 标题具有误导性。不再是直接使用 arr.length 在高级浏览器中变慢了。 javascript loop performance - Why is to decrement the iterator toward 0 faster than incrementing的可能重复 【参考方案1】:

因为在第一种形式中,您每次迭代都访问数组arr 的属性length 一次,而在第二种形式中您只访问一次。

【讨论】:

@samccone - 您看到了什么结果(在哪个浏览器中进行了哪个测试)?我看到缓存长度总是至少一样快,而且通常更快。 好吧,你是对的......看起来它确实是一个混合的结果,但缓存确实有帮助......谢谢XD【参考方案2】:

i &gt; 0i &lt; arr.length 快,并且在循环的每次迭代中都会发生。

您可以通过以下方式减轻差异:

for (var i = 0, len = arr.length; i < len; ++i) ;

这仍然不如向后项目快,但比您的向前选项快。

【讨论】:

看看硬编码测试jsperf.com/array-iteration-direction ...使用你的逻辑应该更快不是吗?【参考方案3】:

如果你想让它们保持相同的速度,你可以这样做进行前向迭代;

for(var i=0, c=arr.length; i<c; i++)

因此,您的脚本不需要在每一步都获取数组的长度。

【讨论】:

啊……有趣的是,您的方法现在获得了最快迭代的桂冠jsperf.com/array-iteration-direction【参考方案4】:

这些同样好:

var arr= [], L= 10000;
while(L>-1) arr[L]= L--;

var arr= [], i= 0;
while(i<10001) arr[i]=i++;

【讨论】:

应该是while(--L&gt;=0) arr[L] = L;【参考方案5】:

我对此并不完全确定,但这是我的猜测:

对于以下代码:

for (var i = 0; i < arr.length; ++i) ;

在运行期间,每次循环通过后都会进行 arr.length 计算。当它独立时,这可能是一个微不足道的操作,但在涉及多个/巨大的数组时可能会产生影响。您可以尝试以下方法吗:

 var numItems = arr.length;
    for(var i=0; i< numItems; ++i)
    
    

在上面的代码中,我们只计算了一次数组长度,并使用计算出的数字进行操作,而不是一遍又一遍地执行长度计算。

再一次,把我的想法放在这里。确实很有趣的观察!

【讨论】:

【参考方案6】:

像下面那样做,它会以同样的方式执行。因为arr.length 在向前的每次迭代中都需要时间。

int len = arr.length;

前进

for (var i = 0; i < len; ++i) 

向后

for (var i = len; i > 0; --i) 

【讨论】:

后面的情况是不对的。我所做的是for(i=len; --i&gt;=0;) for (let i = arr.length; i-- &gt; 0;) 【参考方案7】:

因为您的转发条件每次都必须接收数组的 length 属性,而另一个条件只需要检查“大于零”,这是一项非常快的任务。

当你的数组长度在循环期间没有改变,并且你真的看 ns-perfomance 时,你可以使用

for (var i=0, l=arr.length; i<l; i++)

顺便说一句:您可以使用for (var i = arr.length; i-- &gt; 0; ) 而不是for (var i = arr.length; i &gt; 0; --i),它实际上是从n-1 到0,而不是从n 到1。

【讨论】:

是的,这就是我优化功能的方式。请注意,如果数组是可变的(通常不是),这可能会跳过一两个资源。例如,如果您正在循环遍历循环本身正在添加或删除的元素,这将不起作用。 你甚至可以做for(... ; i-- ;) 所以基本上这意味着如果我不更改数组并将array.length保存在一个变量中,那么向后速度=向前速度?示例: var length = array.length; for(var i = 0 ; i afaik,i&lt;l 是与 i&gt;0 不同的计算,但是它们相当相等(比未缓存的长度更快):jsperf.com/array-iteration-direction/3 - 但有些引擎确实识别并优化了其中一些案例 快进到 2018 年。看起来现代版本的浏览器不会使循环变慢,即使在每次迭代中访问 arr.length 时(您的转发案例)

以上是关于为啥向后迭代数组比向前迭代更快的主要内容,如果未能解决你的问题,请参考以下文章

有没有比使用 np.where 更快的方法来迭代一个非常大的 2D numpy 数组?

为啥在迭代 NumPy 数组时 Cython 比 Numba 慢得多?

OpenCV 矩阵运算是不是比简单的循环迭代更快?

c#在遍历列表时删除元素-向后迭代或使用i--或使用linq同时迭代和删除?

在每次迭代中重新声明变量是不是比在每次迭代后重置它们更快?

为啥聚合+排序比mongo中的查找+排序更快?