Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 72 题:为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因 #121

Open
Rashomon511 opened this issue May 13, 2019 · 47 comments
Labels

Comments

@Rashomon511
Copy link

No description provided.

@lvtraveler
Copy link

  • for 循环没有任何额外的函数调用栈和上下文;

  • forEach函数签名实际上是

array.forEach(function(currentValue, index, arr), thisValue)

它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;

@yygmind yygmind changed the title 为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因 第 72 题:为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因 May 13, 2019
@YouziXR
Copy link

YouziXR commented May 13, 2019

@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?

@schaoss
Copy link

schaoss commented May 13, 2019

@YouziXR 看起來是 jsperf

另外也推薦 jsbench.github.io,都很好用。

@thisisandy
Copy link

找了spec
image
image

其中forEach 里操作了toObject 以及判断是否终止循环条件比for loop 复杂一点。

@Jalever
Copy link

Jalever commented May 13, 2019

@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?

https://jsperf.com/testing-foreach-vs-for-loop

@jjeejj
Copy link
Contributor

jjeejj commented May 13, 2019

这是地址是截图的测试地址:
https://jsperf.com/foreach-compare-for

@mm-bt
Copy link

mm-bt commented May 13, 2019

为哈我测试出来foreach更高呢 chrome版本越高 性能越好

@JasonMa-95
Copy link

牛逼

@JasonMa-95
Copy link

for循环本来就是最原始的循环方案 foreach是基于它封装的

@jjeejj
Copy link
Contributor

jjeejj commented May 14, 2019

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

@LiuMengzhou
Copy link

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

@jjeejj 我理解是普通的业务需求没有那么大的数组,所以性能差距很小。
forEach写法较for简单一点。个人理解

@Zephylaci
Copy link

Zephylaci commented May 14, 2019

@mm-bt 第一个测试我也是forEach稍微快一丝丝,记得以前看过说针对forEach的实现是改过的..
也就是用底层实现的循环(forEach,map)比自己写for循环效率要好..
当时我在浏览器里跑过测试发现的确是这样..(10万以内)
然而刚我又写了一遍类似的..单次的话..for循环是要慢一些..

@jjeejj
Copy link
Contributor

jjeejj commented May 14, 2019

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

@jjeejj
Copy link
Contributor

jjeejj commented May 14, 2019

@mm-bt 我也是,一样的结果,记得以前看过针对forEach的实现是改过的..依稀记得,我在浏览器里跑循环测试时间很多情况下forEach在数量非常大的情况下,比单纯的for要快一些...map也比for快,至少在10万次循环以内是这样

我也是,单独跑时间的话,是 forEach 快些,但是用那些性能测试工具的话,就是 for性能更好
比如 http://jsbench.github.io/https://jsperf.com/ 这两个工具测试

@Zephylaci
Copy link

Zephylaci commented May 14, 2019

@jjeejj
看了你后面的时间测试结果,和我以前测得结果类似..你可以再加上map,10万以内map也会快一些..
今天,我又查了下,发现,测试工具测试的是多次的结果...单次来看的确是forEach快..但是连着执行就会出现问题..10万以内的量级 连续执行10次就会有一倍的差距展示出来..
期待有大佬能解答,为什么会这样,以及循环的这几个阈值是怎么来的..为什么..
参考:
别人验证的文章
别人验证的文章2
v8的建议

var number = 100000;//array大小
var iteranum = 10;//迭代次数
var array = [];
for(let i=0;i<number;i++)
{
    array[i] = i+1;
}

//test cicle
var len = array.length;
//正常for循环
console.time('normal for');
for(let k=0;k<iteranum;k++)
{
    for(let i=0;i<len;i++)
    {
        array[i]+1;
    }
}

console.timeEnd('normal for');

//倒序for循环
console.time('reverse for');
for(let k=0;k<iteranum;k++)
{
    for(let i=len-1;i--;)
    {
        array[i]+1;
    }
}
console.timeEnd('reverse for');

//while循环
console.time('while');
for(let k=0;k<iteranum;k++)
{   
    let i=0;
    while(i<len)
    {
        array[i]+1;
        i++;
    }
}
console.timeEnd('while');

//for-in循环
console.time('for-in');
for(let k=0;k<iteranum;k++)
{
    for(let i in array)
    {
        array[i]+1;
    }
}
console.timeEnd('for-in');

//for each 循环
console.time("for each");
for(let k=0;k<iteranum;k++)
{
    array.forEach(function(e){
        e+1;
    });
}
console.timeEnd("for each");

//map 循环
console.time("map");
for(let k=0;k<iteranum;k++)
{
    array.map(function(e){
        e+1;
    });
}
console.timeEnd("map");

@TH-coder
Copy link

@jjeejj 看到过一个说法,forEach能让代码更为简便和可读性更高,在性能不是特别影响的前提下,代码的可拓展性,可读性等会更为重要,而且随着浏览器引擎的升级,应该forEach的性能会被优化的越来越棒的

@ttma2002
Copy link

简单去看,就是手动控制循环次数,和方法判定结束,肯定是不一样的

@ragnar-document
Copy link

for循环是常见的循环语句forEach和map是在ES5出的,但是在性能上后者不如前者,在次数少的情况下forEach会比for要快,但是到达了十万次时forEach明显就跟不上了。在大数据量的情况下for循环的兼容性和多环境运行表现比较优秀,forEach的优点是在数据量小时占优势,语义话更简洁。循环时没有返回值。map和forEach差不多但是map循环有返回值

@Arrogant128
Copy link

在chrome浏览器上测了一下,当数组length超过3355443时,forEach的耗时是超过for循环的

@sailei1
Copy link

sailei1 commented Jul 18, 2019

一个是判断数组的长度,一个是 判断对象的长度,还有判断对象里面有没有数组下标这个key,
类似于 list 跟 map。

@lxx2013
Copy link

lxx2013 commented Jul 25, 2019

我猜forEach需要的额外内存使得重复实验时触发了垃圾回收,因此稍慢于for

@287
Copy link

287 commented Jul 30, 2019

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

你这个测试里forEach函数里的方法应该是一次都没被调用过 所以执行时间较短

@liuguang2016
Copy link

liuguang2016 commented Jul 31, 2019

for循环是底层写法,效率高。
forEach是封装写法,效率会低一些
`let arrs = new Array(1000000);
console.time('forEach');
arrs.forEach((arr) => {});
console.timeEnd('forEach');

console.time('for');
for (let i = 0,len=arrs.length; i < len; i++) {};
console.timeEnd('for');`
结果:
forEach: 7.42919921875ms
for: 4.7470703125ms

@wulichenyang
Copy link

@jjeejj 看到过一个说法,forEach能让代码更为简便和可读性更高,在性能不是特别影响的前提下,代码的可拓展性,可读性等会更为重要,而且随着浏览器引擎的升级,应该forEach的性能会被优化的越来越棒的

可读性确实很重要,另外函数式编程也是趋势。

@aigle123
Copy link

aigle123 commented Aug 7, 2019

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

@ryouaki
Copy link

ryouaki commented Sep 12, 2019

v8 新版本其实现在性能上已经差不多了。但是forEach需要额外的内存和函数调用。不过新版本得v8给优化了。

@NECKLI
Copy link

NECKLI commented Dec 4, 2019

@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?

https://jsperf.com/

@hduhdc
Copy link

hduhdc commented Dec 13, 2019

foreach效率比for低主要分2个角度说。
2个地方,一个是.net 1.1之前的对值类型的装箱,一个是每次调用GetEnumator方法的函数调用带来的时间消耗,单一次都不消耗时间,但经过大量循环放大后,时间消耗比较明显。
.net 1.1之后的版本,foreach对值类型已经不装箱,不慢了,因为有了yield关键字。
但函数调用带来的堆栈创建内存分配则不可避免。
绝对意义上,for比foreach快,但从.net 1.1之后,这个差距缩小到多一层函数调用而已,不是特别严格的地方,还是用foreach好一点。因为foreach不止可以访问一个数组或List这样循环时能确定长度的集合,也可以访问可迭代的类型,对于一些不需要最开始就确定长度的,这样甚至效率更高,因为不需要在循环开始之前就准备好要循环的数据,而是每次foreach循环获取下一个数据。
其实也不用记什么情况用,多写写程序,应该不难区分用途
借鉴大佬https://me.csdn.net/wuyazhe

@ryouaki
Copy link

ryouaki commented Dec 13, 2019

对于一些不需要最开始就确定长度的,这样甚至效率更高,因为不需要在循环开始之前就准备好要循环的数据,而是每次foreach循环获取下一个数据。
其实也不用记什么情况用,多写写程序,应该不难区分用

我咋觉得哥们走错片场了

@yygmind
Copy link
Contributor

yygmind commented Dec 16, 2019

@iceycc
Copy link

iceycc commented Jan 12, 2020

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

forEach是声明式函数,我们不用关心内部如何去实现的;for循环是命令式函数,我们告诉计算器如何去做

@hduhdc
Copy link

hduhdc commented Jan 13, 2020 via email

@zxhuaman
Copy link

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。
image
image
这种比较可能无法说明问题

@ryouaki
Copy link

ryouaki commented Feb 24, 2020

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。
image
image
这种比较可能无法说明问题

在chrome v8 6.x以后。对forEach的实现进行了改变。目前在v8 6.x forEach和for性能是一样的

@whosesmile
Copy link

  1. 问题不成立,没有谁远高于谁;
  2. 最高票的测试代码有问题: new Array(N).forEach压根不循环; i < arr.length如果大循环应该提前定义长度变量;

@luckC-newing
Copy link

捕获
加一 10万以下 性能差不多
10万以上for性能秒杀foreach

@Vivomo
Copy link

Vivomo commented Mar 24, 2020

找了spec
image
image

其中forEach 里操作了toObject 以及判断是否终止循环条件比for loop 复杂一点。

forEach不能终止循环, 何来判断条件一说

@monxiaolee
Copy link

既然foreach没法终止(try catch)。没有for循环性能好,为啥大家大家还用呢

@hycript
Copy link

hycript commented May 30, 2020

对于高票的答案稍微改造了下, 上面有同学提出 new Array(N) 对于forEach来讲是不会循环的. 所以改造了下 数组的创建方式. 另外for循环中也做了些改造. 现在的测试结果基本就很明显了.

10万次循环的对比结果:

let arrs = Array.from({length: 100000});

console.time('for');
let len = arrs.length;
for (let i = 0; i < len; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');
for: 2.68310546875ms
forEach: 2.286376953125ms

100万次的对比结果

for: 2.40771484375ms
forEach: 15.508056640625ms

1000万次的对比结果

for: 7.268798828125ms
forEach: 153.18212890625ms

@soraly
Copy link

soraly commented Jun 29, 2020

为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因

  • forEach需要额外的内存和函数调用,上下文作用域等等,所以会拖慢性能
  • 新版浏览器已经优化的越来越好,性能上的差异会越来越小

@fhuy
Copy link

fhuy commented Sep 4, 2020

为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因

  • forEach需要额外的内存和函数调用,上下文作用域等等,所以会拖慢性能
  • 新版浏览器已经优化的越来越好,性能上的差异会越来越小

赛高!

@xianjing123
Copy link

xianjing123 commented Dec 29, 2020

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

如果按照之前的方法测试确实如此,不过给数组填充内容后,10万的数据量结果相差甚微,在100万数据量for循环大于forEach

let arrs = new Array(100000).fill(1);

console.time('for');
let for1 = []
for (let i = 0; i < arrs.length; i++) {
    for1.push(arrs[i])
};
console.timeEnd('for');

console.time('forEach');
let for2 = []
arrs.forEach((arr, index) => {
 for2.push(arr)
});
console.timeEnd('forEach');
for: 2.760986328125 ms
forEach: 2.88427734375 ms

@MrLeihe
Copy link

MrLeihe commented Apr 26, 2021

在 node 和浏览器环境下,小于百万级的数据遍历,我这边测出来的结果始终是 forEach 优于 for

@xld1393679430
Copy link

xld1393679430 commented May 24, 2021

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

一样的 不管是先赋值还是直接判断length都是上面的结论。 甚至在一千万时赋值length比直接判断耗时更长, 有点搞不懂

@yft
Copy link

yft commented Aug 1, 2021

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

如果 使用 new Array(100000).fill(1) ,会发现,两者的差距并不大

当仅仅是 new Array(100000) 时, forEach 并不会执行 callback

@deyinliu
Copy link

这个问题有点问题,Chrome环境for比forEach快、Safari两种方式差异不明显、node环境forEach比for快

@runner-up-js
Copy link

let arrs = new Array(100000);
for (let i = 0; i < arrs.length; i++) {
arrs[i] = i
};
console.time('for');
for (let i = 0; i < arrs.length; i++) {
};
console.timeEnd('for');

console.time('forEach');

arrs.forEach((arr) => {

});
console.timeEnd('forEach');

for: 1ms
forEach: 2ms
奇怪我把楼上的代码复制一下,改了改 时间久是 for效率更好了 而且 有值的for循环比无值的for 速度更快

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests