美文网首页
for 循环性能探究与优化

for 循环性能探究与优化

作者: holidayPenguin | 来源:发表于2021-07-06 19:41 被阅读0次

第一个循环

在我们开始学习编程的时候就接触过for循环

var arr = new Array(10).fill(1)

for (var i = 0 ; i < arr.length ; i++ ) {
  console.log(arr[i])
}

这是很简单的一段,记得当初在用java或者C++的时候说过为了提高访问效率需要把数组长度缓存到一个变量中

var arr = new Array(10).fill(1)
var len = arr.length

for (var i = 0 ; i < len ; i++ ) {
  console.log(arr[i])
}

那么在我们前端的浏览器、Node里面也是这样吗?

确定for循环写法

因为ES6中新加了let声明变量的方式,所以有些方式需要增加以下对比;同时使用数组的索引访问增加真实使用场景时间消耗。

var arr = Array.from(new Array(999999)).map((_, i) => i)

// 不缓存数组长度 var
for (var i1 = 0; i1 < arr.length; i1++) {(arr[i1])}

// 缓存数组长度 var
for (var i2 = 0, len2 = arr.length; i2 < len2; i2++) {(arr[i2])}

// 不缓存数组长度 let
for (let i3 = 0; i3 < arr.length; i3++) {(arr[i3])}

// 缓存数组长度 let
for (let i4 = 0, len4 = arr.length; i4 < len4; i4++) {(arr[i4])}

// 数组倒序 var
for (var i5 = arr.length - 1; i5 >= 0; i5--) {(arr[i5])}

// 数组倒序 let
for (let i6 = arr.length - 1; i6 >= 0; i6--) {(arr[i6])}

// 真值判断 var
for (var i6 = 0, item; item = arr[i6]; i6++) {(arr[i6])}

// 真值判断 let
for (let i6 = 0, item; item = arr[i6]; i6++) {(arr[i6])}

// forEach
a.forEach(function(item7) {(item7)

// map
a.map(function(item7) {(item7)

// for of
for(item8 of a) {(item8)}

// while var
var i = 0
while(i < arr.length) {
  (arr[i++])
}

// while let
let i = 0
while(i < arr.length) {
  (arr[i++])
}

// while let 缓存数组长度
let i = 0, len = arr.length
while(i < len) {
  (arr[i++])
}

// for in
for(i8 in a) {(arr[i8])}

浏览器中运行时间

时间判断:使用console.timeconsole.timeEnd记录每种循环方式的整体执行时间。

内存消耗判断:使用process.memoryUsage()比对执行前后内存中已用到的堆的差值。当然要先判断process是否存在 typeof process !== 'undefined'才能使用

局部作用域

# Chrome
局部作用域 不缓存数组长度 var: 3.096923828125 ms
局部作用域 缓存数组长度 var: 2.745849609375 ms
局部作用域 不缓存数组长度 let: 3.07373046875 ms
局部作用域 缓存数组长度 let: 5.197998046875 ms
局部作用域 数组倒序 var: 2.751953125 ms
局部作用域 数组倒序 let: 2.7138671875 ms
局部作用域 真值判断 var: 0.038818359375 ms
局部作用域 真值判断 let: 0.034912109375 ms
局部作用域 forEach: 15.8369140625 ms
局部作用域 map: 30.466064453125 ms
局部作用域 for of: 62.18017578125 ms
局部作用域 while var: 3.120849609375 ms
局部作用域 while let: 3.925048828125 ms
局部作用域 while let 缓存数组长度: 2.635009765625 ms
局部作用域 for in: 367.190185546875 ms

# Firefox
局部作用域 不缓存数组长度 var:285 毫秒
局部作用域 缓存数组长度 var:129 毫秒
局部作用域 不缓存数组长度 let:249 毫秒
局部作用域 缓存数组长度 let:136 毫秒
局部作用域 数组倒序 var:135 毫秒
局部作用域 数组倒序 let:136 毫秒
局部作用域 真值判断 var:1 毫秒
局部作用域 真值判断 let:0 毫秒
局部作用域 forEach:4 毫秒
局部作用域 map:20 毫秒
局部作用域 for of:251 毫秒
局部作用域 while var:235 毫秒
局部作用域 while let:238 毫秒
局部作用域 while let 缓存数组长度:141 毫秒
局部作用域 for in:1556 毫秒

全局作用域

# Chrome
不缓存数组长度 var: 17.60791015625 ms
缓存数组长度 var: 13.378173828125 ms
不缓存数组长度 let: 18.7080078125 ms
缓存数组长度 let: 21.89697265625 ms
数组倒序 var: 26.673095703125 ms
数组倒序 let: 13.211181640625 ms
真值判断 var: 0.013671875 ms
真值判断 let: 0.012939453125 ms
forEach: 25.43505859375 ms
map: 41.006103515625 ms
for of: 38.648193359375 ms
while var: 11.234130859375 ms
while let: 15.410888671875 ms
while let 缓存数组长度: 13.287841796875 ms
for in: 394.052978515625 ms

# Firefox
不缓存数组长度 var:928 毫秒
缓存数组长度 var:948 毫秒
不缓存数组长度 let:428 毫秒
缓存数组长度 let:250 毫秒
数组倒序 var:907 毫秒
数组倒序 let:192 毫秒
真值判断 var:0 毫秒
真值判断 let:1 毫秒
forEach:4 毫秒
map:20 毫秒
for of:362 毫秒
while var:812 毫秒
while let:733 毫秒
while let 缓存数组长度:714 毫秒
for in:1429 毫秒
  • 在全局作用域中
    • let 整体好于 var
  • 局部作用域中
    • 用时整体比全局作用域短
    • let 和 var无明显区别(性能同全局环境中let差不多);
    • Firefox中不缓存数组长度虽然好于在全局环境,但是 let 还是快 var 很多
    • Firefox中缓存数组还是有点效果的

总结:全局环境中var需要在全局作用域中查找,let是块作用域中查找,所以性能好;但是在方法的局部作用域中无明显差别。(问题是变量查找不是应该按照作用域链向上查找吗,性能应该差不多吧)

在node中运行时间

局部作用域 不缓存数组长度 var: 3.777ms
局部作用域 缓存数组长度 var: 2.765ms
局部作用域 不缓存数组长度 let: 4.521ms
局部作用域 缓存数组长度 let: 6.082ms
局部作用域 数组倒序 var: 3.672ms
局部作用域 数组倒序 let: 6.448ms
局部作用域 真值判断 var: 0.181ms
局部作用域 真值判断 let: 0.148ms
局部作用域 forEach: 27.809ms
局部作用域 map: 37.32ms
局部作用域 for of: 35.697ms
局部作用域 while var: 3.953ms
局部作用域 while let: 2.643ms
局部作用域 while let 缓存数组长度: 2.58ms
局部作用域 for in: 458.263ms

不缓存数组长度 var: 10.03ms
缓存数组长度 var: 17.274ms
不缓存数组长度 let: 15.581ms
缓存数组长度 let: 17.644ms
数组倒序 var: 14.843ms
数组倒序 let: 14.887ms
真值判断 var: 0.016ms
真值判断 let: 0.02ms
forEach: 45.607ms
map: 54.476ms
for of: 57.174ms
while var: 9.187ms
while let: 13.486ms
while let 缓存数组长度: 15.922ms
for in: 640.726ms
  • 是否在全局变量,只对使用for循环的方法有影响(局部变量的时候会快点)
  • 是否使用let还是var,对运行时间没有太大影响,使用let还会慢一点。

总结

对于for循环的优化

  • 是否缓存数组长度没有太大的影响,因为各个编译器对这一块可能做了优化
  • 使用let还是var在浏览器(let 全局作用域的时候会快一点)和Node(无影响)表现不一,
  • 局部作用域中整体比全局作用域好
  • 只有一个方法是表现最好的真值判断法,各个端都很理想

因此在数据量相对小的地方可以使用for循环,当较大是可以使用真值判断法,当有复杂需求时可能还要回到for循环

数组操作性能探究

// TODO

局部环境代码

/* eslint-disable */

var a = Array.from(new Array(999999)).map((_, i) => i)

function loop(key, fun) {
  console.time(key)
  // if (typeof process !== 'undefined') {
  //   const startHeap = process.memoryUsage().heapUsed
  //   fun()
  //   const endHeap = process.memoryUsage().heapUsed
  //   const heapDiff = endHeap - startHeap
  //   console.timeEnd(key)
  //   console.log('\t已用到的堆的差值: ', heapDiff)
  // } else {
    fun()
    console.timeEnd(key)
  // }
}

loop('局部作用域 不缓存数组长度 var', _ => {
  for (var i1 = 0; i1 < a.length; i1++) {(a[i1])}
})

loop('局部作用域 缓存数组长度 var', _ => {
  for (var i2 = 0, len2 = a.length; i2 < len2; i2++) {(a[i2])}
})

loop('局部作用域 不缓存数组长度 let', _ => {
  for (let i3 = 0; i3 < a.length; i3++) {(a[i3])}
})

loop('局部作用域 缓存数组长度 let', _ => {
  for (let i4 = 0, len4 = a.length; i4 < len4; i4++) {(a[i4])}
})

loop('局部作用域 数组倒序 var', _ => {
  for (var i5 = a.length - 1; i5 >= 0; i5--) {(a[i5])}
})

loop('局部作用域 数组倒序 let', _ => {
  for (let i6 = a.length - 1; i6 >= 0; i6--) {(a[i6])}
})

loop('局部作用域 真值判断 var', _ => {
  for (var i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
})

loop('局部作用域 真值判断 let', _ => {
  for (let i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
})

loop('局部作用域 forEach', _ => {
  a.forEach(function(item7) {(item7)})
})

loop('局部作用域 map', _ => {
  a.map(function(item7) {(item7)})
})

loop('局部作用域 for of', _ => {
  for(item8 of a) {(item8)}
})

loop('局部作用域 while var', _ => {
  var i = 0
  while(i < a.length) { (a[i++]) }
})

loop('局部作用域 while let', _ => {
  let i = 0
  while(i < a.length) { (a[i++]) }
})

loop('局部作用域 while let 缓存数组长度', _ => {
  let i = 0, len = a.length
  while(i < len) { (a[i++]) }
})

// var a1 = Array.from(new Array(99999)).map((_, i) => i)

loop('局部作用域 for in', _ => {
  for(i8 in a) {(a[i8])}
})

全局环境代码

/* eslint-disable */

var a = Array.from(new Array(999999)).map((_, i) => i)

console.time('不缓存数组长度 var')
for (var i1 = 0; i1 < a.length; i1++) {(a[i1])}
console.timeEnd('不缓存数组长度 var')

console.time('缓存数组长度 var')
for (var i2 = 0, len2 = a.length; i2 < len2; i2++) {(a[i2])}
console.timeEnd('缓存数组长度 var')

console.time('不缓存数组长度 let')
for (let i3 = 0; i3 < a.length; i3++) {(a[i3])}
console.timeEnd('不缓存数组长度 let')

console.time('缓存数组长度 let')
for (let i4 = 0, len4 = a.length; i4 < len4; i4++) {(a[i4])}
console.timeEnd('缓存数组长度 let')

console.time('数组倒序 var')
for (var i5 = a.length - 1; i5 >= 0; i5--) {(a[i5])}
console.timeEnd('数组倒序 var')

console.time('数组倒序 let')
for (let i6 = a.length - 1; i6 >= 0; i6--) {(a[i6])}
console.timeEnd('数组倒序 let')

console.time('真值判断 var')
for (var i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
console.timeEnd('真值判断 var')

console.time('真值判断 let')
for (let i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
console.timeEnd('真值判断 let')

console.time('forEach')
a.forEach(function(item7) {(item7)})
console.timeEnd('forEach')

console.time('map')
a.map(function(item7) {(item7)})
console.timeEnd('map')

console.time('for of')
for(item8 of a) {(item8)}
console.timeEnd('for of')

console.time('while var')
var i9 = 0
while(i9 < a.length) {(a[i9++])}
console.timeEnd('while var')

console.time('while let')
let i10 = 0
while(i10 < a.length) {(a[i10++])}
console.timeEnd('while let')

console.time('while let 缓存数组长度')
let i11 = 0, len = a.length
while(i11 < len) {(a[i11++])}
console.timeEnd('while let 缓存数组长度')

// var a1 = Array.from(new Array(99999)).map((_, i) => i)

console.time('for in')
for(i8 in a) {(a[i8])}
console.timeEnd('for in')

参考

https://juejin.cn/post/6844903776185221127#heading-5
https://juejin.cn/post/6844903651232710670
https://www.jb51.net/article/80854.htm

相关文章

网友评论

      本文标题:for 循环性能探究与优化

      本文链接:https://www.haomeiwen.com/subject/bzvgultx.html