前言
众所周知,JavaScript中有常见的5种循环,传统的for...i循环,while循环,for...in循环,for...of循环,还有一个forEach。在网上看到了很多关于它们的性能的比较,我觉得还可以再改进一下。 比如下面这样的代码:
let arr = new Array(999999).fill(1)
console.time('forTime')
for(let i = 0; i< arr.length; i++){}
console.timeEnd('forTime')
console.time('whileTime')
let i = 0
while(i< arr.length){
i ++
}
console.timeEnd('whileTime')
/* 输出
* forTime: 4.864990234375 ms
* whileTime: 8.35107421875 ms
*/
按照常理,一个东西被循环迭代了一遍,那么它会有缓存在,比如CPU的缓存中,内存的热区,软件中的缓冲池中等等(我不相信V8引擎中没有缓存池以提升性能,虽然我没读过它的源码)。 那么, 在这段代码中,同样一个数组,for循环一遍,while循环一遍,那么按照常理,for要比while慢,但是诡异的是作者的测试结论,竟然是while的耗时是for的一倍, 这我就理解不了了。
上手自己验证寻找答案
既然看不懂别人的代码和结论,那我就自己写一个,也不复杂,我一个循环单独放在一个文件中,写好了之后,使用node命令去分别去执行它们, 我不会在一段代码中将一个数组使用不同的循环方式分别遍历一遍,这样的做法,其结论还需要我研究更多东西之后再测试。
5种循环性能大比拼
前提:
- 我的代码是用TS写的,然后使用tsc编译成JS再执行,这些代码中使用了
new Array<number>(999999).fill(1)这样的语法,tsc编译时要增加-target es6选项。 - 我的操作系统是Windows 10专业版64位系统。
- 我的执行环境是Powershell 7.4.6。
- 我的tsc的版本是5.6.3。
- 我的node的版本是18.20.3。
不说清楚这些前提,直接给测试结论,那就是耍流氓。
来,看我2024年11月25日更新了的这些代码:
// 性能测试:常规for循环
let numbers_for_gen: number[] = new Array<number>(999999).fill(1);
console.time('for...normal');
for (let i = 0; i < numbers_for_gen.length; i++) {
}
console.timeEnd('for...normal');
// 性能测试:常规while循环
let numbers_while: number[] = new Array<number>(999999).fill(1);
console.time('while...normal');
let i = 0;
while (i < numbers_while.length) {
i++;
}
console.timeEnd('while...normal');
// 性能测试:常规 for...in 循环
let numbers_for_in: number[] = new Array<number>(999999).fill(1);
console.time('for_in...normal');
for (let num in numbers_for_in) {
}
console.timeEnd('for_in...normal');
// 性能测试:常规 for...of 循环
let numbers_for_of: number[] = new Array<number>(999999).fill(1);
console.time('for_of...normal');
for (let num of numbers_for_of) {
}
console.timeEnd('for_of...normal');
// 性能测试:常规 forEach 循环
let numbers_forEach: number[] = new Array<number>(999999).fill(1);
console.time('numbers_forEach...normal');
numbers_forEach.forEach(item => {})
console.timeEnd('numbers_forEach...normal');
每个测试文件,分别执行10次,然后分析这10次的累计耗时、最小耗时、最大耗时、平均耗时,这样的测试才是公平的,好了,这是结论:
关注点
一般情况下,差别100万的数组大小,分别执行10次,1000万的数据量,我们关注它们的合计耗时和平均耗时就可以了(因为如果真的负荷比较大的情况下,我们更需要关注它们的内存开销和I/O开销而不是CPU开销,除非这是用来处理大数据或者搞算法搞人工智能和AI的):
- 常规的for...i循环性能最好
- while循环性能也不错
- forEach循环性能也还行
- for...of循环似乎差了一点点
- for...in循环耗时较大
最终结论
这些个各种循环,各种语法糖,各种魔法,你平时可以随便用,不必在乎它们的性能差异,因为更多的时候,我们应该关注页面卡不卡顿,接口是否流畅,内存开销是否正常(尤其是这一点值得重点关注,我们不应该一次从数据库或者磁盘文件或者网络接口中读入这么大的数据量,这不是正常人干的事情,除了让用户卡死、内存溢出、硬件资源耗尽之外没有任何好处),就可以了。
特别说明: 一个星期之前,我做过的测试,性能差别没这么大,当时我定义的是any数组,里面fill填充进去的1,几种循环性能差异不大,这一次用的是TS的泛型法,里面填充的number,结果性能测试的结论出现了较大的偏差。