我一直认为在 JavaScript 中缓存数组的长度是一个好主意(特别是在 for 循环的情况下),因为计算数组长度的成本很高。
示例
for (var i = 0; i < arr.length; i++) { }
// vs
for (var i = 0, arrLength = arr.length; i < arrLength; i++) { }
但是,我认为也许 length
属性仅在创建和更改数组时更新。因此,与读取存储在变量中的操作相比,读取它不应该是太昂贵的操作(与其他语言中可能需要在内存中查找以找到某些内容的结尾的其他方法相反,例如 strlen( )
在 C) 中。
我有两个问题。我也对它的工作原理很感兴趣,所以请不要用过早的优化棒来打击我。
假设浏览器中的 JavaScript 引擎。
- 在 JavaScript 中缓存数组的
length
属性有什么好处吗?与读取对象的属性相比,读取局部变量是否涉及更多内容?
-
length
属性是否在创建时以及在不返回新数组的 shift()
和 pop()
类型方法上进行了简单更改,否则简单地存储为整数?
I always assumed caching the length of an array in JavaScript is a good idea (especially in the condition of a for
loop) because of the expensiveness of calculating the length of an array.
Example
for (var i = 0; i < arr.length; i++) { }
// vs
for (var i = 0, arrLength = arr.length; i < arrLength; i++) { }
However, I thought perhaps the length
property is only updated on creation and alteration of the array. Therefore, reading it shouldn't be too expensive an operation as opposed to reading it stored in a variable (as opposed to other methods in other languages that may need to seek in memory to find the end of something, e.g. strlen()
in C).
I have two questions. I am also interested in how this works, so please don't hit me with the premature optimisation stick.
Assume the JavaScript engines in browsers.
- Is there any advantage to caching the
length
property of an array in JavaScript? Is there much more involved in reading a local variable over an object's property?
- Is the
length
property simply altered on creation and on shift()
and pop()
type methods that don't return a new array and otherwise simply stored as an integer?
发布评论
评论(6)
好吧,我会说它很贵,但后来我写了一个小测试@ jsperf.com 令我惊讶的是,在 Chrome 中使用 i 实际上更快,而在 FF(4) 中这并不重要。
我怀疑长度存储为整数(Uint32)。来自 ECMA 规范(262 第 5 版,第 121 页):
唷!我不知道我是否习惯了这样的语言......
最后,我们总是有我们的好旧落后的浏览器。在 IE (9,8,7) 中,缓存长度确实更快。我说,这是不使用 IE 的众多原因之一。
Well, I would have said it was expensive, but then I wrote a little test @ jsperf.com and to my surprise using
i<array.length
actually was faster in Chrome, and in FF(4) it didn't matter.My suspicion is that length is stored as an integer (Uint32). From the ECMA-specs (262 ed. 5, page 121):
Phew! I don't know if I ever get used to such language ...
Finally, we always have our good old lagging behind browser. In IE (9, 8, 7) caching the length is really faster. One of many more reasons to not use IE, I say.
TL;DR:
据我所知,似乎数组的长度是在内部缓存的(至少在 V8 中)..
(详细信息?请继续阅读:))
所以,这个问题已经解决了在我的脑海中思考了几次,我决定找到问题的根源(至少在一个实现中)。
挖掘 V8 源代码产生了 JSArray 类。
我假设数组元素的类型决定了它是快还是慢。我在
set_has_fast_elements
(set_bit_field2(bit_field2() | (1 << kHasFastElements))
) 中设置了一个位标志,这就是我想的地方当我在谷歌代码中查找并且本地没有源代码时,我会画出挖掘线。现在,似乎任何时间任何对数组(它是
JSObject
的子类)进行操作,调用NormalizeElements()
,执行以下操作:因此,在回答您的问题时:
length
属性数组的(除非你正在做一些奇怪的事情,这会迫使它变慢(我不确定这些条件是什么)) - 话虽如此,我很可能会继续缓存 < code>length 直到我有机会了解所有操作系统浏览器实现;)length
属性似乎在any之后发生了改变> 对对象的操作编辑:
顺便说一句,看起来一个“空”数组实际上被分配了 4 个元素:
我不确定一旦超出界限数组会增长多少个元素 - 我没有挖那么深: )
TL;DR:
From what I can gather, it seems like the length of the array is cached internally (at least in V8)..
(Details? Read on :))
So, this question has dinged around in my head a few times and I've decided to get to the root of the problem (at least, in one implementation).
Digging around V8 source yielded the JSArray class.
I'm making an assumption that the type of array elements dictates whether it's fast or slow. I got down to a bit flag being set in
set_has_fast_elements
(set_bit_field2(bit_field2() | (1 << kHasFastElements))
), which is where I figured I'd draw the digging line as I was looking in google code and don't have the source locally.Now, it seems that any time any operation is done on the array (which is a child class of
JSObject
, a call is made toNormalizeElements()
, which executes the following:So, in answering your questions:
length
property of an array (unless you're doing something odd that would force it to beslow
(I'm not sure what those conditions are) - having said that, I'll most likely continue to cachelength
until I get a chance to go through all OS browser implementations ;)length
property seems to be altered after any operation on the object.Edit:
On a side note, it seems that an "empty" array is actually allocated to have 4 elements:
I'm not sure how many elements the array grows by once the bounds have been exceeded - I didn't dig that deep :)
另一组性能测试。该循环是通过一个空循环对一百万个随机数的数组完成的。
在 Chrome 中,具有缓存和非缓存长度的循环的时钟时间几乎相同,因此我猜测它是 V8优化缓存长度。
在 Safari 和 Firefox 中,缓存长度始终比非缓存版本快 2 倍左右。
Another set of performance tests. The loop is done over an array of million random numbers with an empty loop.
In Chrome, the loops with cached and non-cached lengths clock pretty much the same times, so I am guessing it's a V8 optimization to cache the length.
In Safari and Firefox, the cached length was consistently about 2x faster than the non-cached version.
本文通过向 IRHydra 询问生成的代码来研究 V8 和 Chrome 中的自动缓存:
他发现在某些条件下手动缓存
.length
实际上增加了开销而不是提高性能!但无论如何,这种微观优化不太可能为您的用户带来任何显着的收益。为了他们的利益,也为了您的利益,请重点关注易于阅读的代码,并在代码中使用良好的数据结构和算法!
避免过早优化:专注于优雅的代码,直到出现性能问题。然后,通过分析找出瓶颈,然后优化那部分代码。
This article investigates automatic caching in V8 and Chrome, by asking IRHydra for the generated code:
He found that under certain conditions manually caching the
.length
actually added overhead rather than improving performance!But anyway, this kind of micro-optimization is unlikely to achieve any noticeable gain for your users. For their benefit, and for yours, focus instead on code that is clear to read, and using good data structures and algorithms in your code!
Avoid premature optimisation: Focus on elegant code until a performance issue arises. Only then, seek out the bottleneck through profiling, and then optimise just that part of the code.
请注意:
在某些浏览器上(我在 Safari、IE 和 Opera 中注意到了这一点),您可以通过缓存 for 循环声明内的长度来提高速度:
我将上面的 @KooiInc 的 jsperf 测试编辑为 添加此案例。
Just a note:
On some browsers (I've noticed it in Safari, IE, and Opera), you can get a speed boost by caching the length inside the for loop declaration:
I edited @KooiInc's jsperf test above to add this case.
请注意不要假设这对于所有可迭代集合都是如此。例如,缓存 HTMLCollection 的长度在 Chrome(版本 41)中快了 65%,在 Firefox(版本 36)中快了 35%。
http://jsperf.com/array-length-in-loop-dom
Take care not to assume that this is true for all iterable collections. For example caching the length of an HTMLCollection is 65% faster in Chrome (Version 41) and 35% faster in Firefox (Version 36).
http://jsperf.com/array-length-in-loop-dom