缓存 Array.length 是老生常谈的小优化

问题

缓存 Array.length 是老生常谈的小优化。

// 不缓存
for (var i = 0; i < arr.length; i++) {
...
}

// 缓存
var len = arr.length;
for (var i = 0; i < len; i++) {
...
}

// 或者
for (var i = 0, len = arr.length; i < len; i++) {
...
}

但以前写过 Java 的笔者一直对这种破碎的写法感到不适,也对这种写法的实际优化效果产生疑问。

且推崇这种写法的朋友似乎很多也是“前辈这么说+自己想了一下觉得有道理”。

由于 for 循环搭配 Array.length 是极度常用的 JavasScript 代码,所以还是有必要搞清楚的。

结论

经过一番摸索后笔者得到的结论是:缓存 Array.lengh 对优化的影响没有想象中的大,甚至可能会有所减慢。

理由

从测试结果上看

stackoverflow 上也有这个讨论,For-loop performance: storing array length in a variable 。

accepted 的答案是说缓存会起到加速的结果,给出了 jsPerf 测试。

但是有答案反对,也给出了 jsPerf 测试。

两个答案的区别在于“循环不变量代码移动(Loop-invariant code motion)”,accepted 答案的测试循环里没有访问到数组,是不实际的,后面会讲到。

从另一篇文章 Shoud I have to cache my array’s length? 的测试结果也可以看出缓存差别不大。

还有这篇 JavaScript‘s .length Property is a Stored Value

从 V8 的中间代码分析

这篇文章 How the Grinch stole array.length access 从 V8 的 hydrogen 探讨 Array.length 在 for 循环中的处理。

正如上面提到的“循环不变量代码移动”,V8 引擎会聪明的把能确定不变的代码移到循环外。

所以像下面这种代码也不会影响引擎对 Array.length 的优化:

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

而当在循环中调用函数时,V8 会尝试将函数进行内联,从而继续进行“循环不变量代码移动”优化。
但当函数不可内联时,V8 就没辙了,每次循环都要重新计算一遍 length

function BLACKHOLE(sum, arr) {
  try { } catch (e) { }
}

function uncached(arr) {
  var sum = 0;
  for (var i = 0; i < arr.length; i++) {
    sum += arr[i];
    if (sum < 0) BLACKHOLE(arr, sum);
  }
  return sum;
}

但这时即便是在循环外缓存了 length 也是没有用的,引擎没法预判数组的变化,当需要访问数组元素时会触发 bounds check ,从而照样要计算一遍 length 。所以缓存 length 是没有用的。

甚至,由于多了一个变量,底层的寄存器分配器每次循环还要多一次恢复这个变量。当然这个只有在大规模的情况下才会看出区别。

结尾

当然这篇文章也有局限性,仅仅讨论了 V8 引擎,也没有讨论访问 length 代价更高的 HTMLCollection 。但这已经足够说明两者差别不大,一般情况下我们不用再局限于缓存的写法,可以放开来按照自己喜欢的方式去写循环了。

【完】

原文:http://div.io/topic/966

时间: 2024-10-14 13:13:24

缓存 Array.length 是老生常谈的小优化的相关文章

博客园博客小优化

博客园博客小优化 Fork me on Github 页首Html 代码部分增加以下代码: <a href="https://github.com/WeihanLi" target="_blank"> <img style="position: fixed; top: 0; right: 0; border: 0;z-index:233" src="https://github-camo.global.ssl.fast

Lichee (六) 配置内核时的一点小优化

我们在分析<Lichee(二) 在sun4i_crane平台下的编译 >的时候,居然没有一个步骤是在配置内核 make ARCH=arm menuconfig 仔细的读过的代码的会发现,在build_kernel有这么一段话 if [ ! -e .config ]; then echo -e "\n\t\tUsing default config... ...!\n" cp arch/arm/configs/sun4i_crane_defconfig .config fi

Popush前端小优化-邹豪风

[实现篇] 实现功能 实现Popush用户注册后自动登录功能. 实现思路 阅读Popush代码的前端部分,主要观察了Views中关于注册和登录两个部分向后台传递数据的方式.发现该项目前后端交互的机理是:客户端利用socket.emit发送数据到服务器端,服务器端利用socket.on进行事件响应并处理相关数据. 客户端发送数据: 1 app.socket.emit('register', { 2 name: name, 3 password: pass, 4 }); 服务器端响应: 1 sock

Count and Say (Array Length Encoding) -- LeetCode

The count-and-say sequence is the sequence of integers beginning as follows:1, 11, 21, 1211, 111221, ... 1 is read off as "one 1" or 11.11 is read off as "two 1s" or 21.21 is read off as "one 2, then one 1" or 1211. Given an

【转】The magic behind array length property

Developer deals with arrays every day. Being a collection, an important property to query is the number of items: Array.prototype.length. In JavaScript the length does not always indicate the number of existing elements (for sparse arrays) and modify

[MySQL5.6] 最近对group commit的小优化

[MySQL5.6] 最近对group commit的小优化 http://www.tuicool.com/articles/rEZr2q 最近花了一些时间在做MySQL Group Commit的优化,关于Group commit的原理,这里不再赘述,有兴趣的可以翻阅我之前的博客 http://mysqllover.com/?p=581,这里简单描述下两点优化,主要基于MySQL5.6.16 1.优化binlog_order_commits=0并且sync_binlog>0时的性能  我们知道

Windows简单小优化

1.更改计算机默认软件安装位置 这些都是要修改注册表的. 你可以参阅windows注册表管理手册. 摘选: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders 显示所有启动组的重要文件夹位置,某些键值含有%USERPROFILE%变量,这个文件为系统指向"C:\Documents and Settings\bootuser(启动用户)"文件夹(不含引号),修改之,

KMP算法的正确性证明及一个小优化

直接把作业帖上来是不是有点不太公道呀... 无所谓啦反正各位看着开心就行 KMP算法 对于模式串$P$,建立其前缀函数$ N$ ,其中$N [q] $ 表示在$P$中,以$q$位置为结束的可以匹配到前缀的最长后缀的长度(也可以理解为那个前缀的结束位置),在匹配中,若$P[i]$与$S[j]$失配,则令$i=N [i-1] +1$ ,否则$i=i+1,j=j+1$ 现考虑如何构造$N$ ,设当前以计算出$N[1..i-1]$ ,则令$k=N[i-1]$ ,若 $P[k+1]=P[i]$,则令$N[

js代码小优化

今天真坑,老大请了两天假,来了之后指指点点,不过人家说的倒是很是到位 好不容易把嵌套小窗口登陆注册功能,做完了,直接调之前写好的登陆注册功能,也就是页面跳转 并不是ajax异步登陆 说让改成ajax ,返回json格式.也就是使用@ResponseBody罢了 恩,没错,改吧,页面直接用的freemarker取值也是有为题了,毕竟ajax后台model,put();的值是取不出来的. 解决完之后毕竟登陆注册小窗口是一个抽取出来的html,那个页面需要直接include进来罢了. OK.我自我感觉