JavaScript 中读取数组的“length”属性真的那么昂贵吗?

发布于 2024-11-02 20:04:43 字数 675 浏览 0 评论 0 原文

我一直认为在 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 引擎。

  1. 在 JavaScript 中缓存数组的 length 属性有什么好处吗?与读取对象的属性相比,读取局部变量是否涉及更多内容?
  2. 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.

  1. 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?
  2. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

回忆追雨的时光 2024-11-09 20:04:43

好吧,我会说它很贵,但后来我写了一个小测试@ jsperf.com 令我惊讶的是,在 Chrome 中使用 i 实际上更快,而在 FF(4) 中这并不重要。

我怀疑长度存储为整数(Uint32)。来自 ECMA 规范(262 第 5 版,第 121 页):

每个数组对象都有一个
length 属性,其值始终是小于 232 的非负整数。长度属性的值为
数字大于名称
每个名称为数组的属性
指数;每当数组的属性
对象被创建或更改,其他
属性根据需要进行调整
来维持这个不变量。
具体来说,每当财产
添加的名称是数组索引,
长度属性改变,如果
必要的,比
该数组索引的数值;和
每当 length 属性为
已更改,名称为的每个属性
其值不是的数组索引
小于新长度
自动删除。这个约束
仅适用于自己的财产
数组对象,不受
长度或数组索引属性
可能继承自其原型

唷!我不知道我是否习惯了这样的语言......

最后,我们总是有我们的好旧落后的浏览器。在 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):

Every Array object has a
length property whose value is always a nonnegative integer less than 232. The value of the length property is
numerically greater than the name of
every property whose name is an array
index; whenever a property of an Array
object is created or changed, other
properties are adjusted as necessary
to maintain this invariant.
Specifically, whenever a property is
added whose name is an array index,
the length property is changed, if
necessary, to be one more than the
numeric value of that array index; and
whenever the length property is
changed, every property whose name is
an array index whose value is not
smaller than the new length is
automatically deleted. This constraint
applies only to own properties of an
Array object and is unaffected by
length or array index properties that
may be inherited from its prototypes

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.

痞味浪人 2024-11-09 20:04:43

TL;DR:

据我所知,似乎数组的长度是在内部缓存的(至少在 V8 中)..

(详细信息?请继续阅读:))

所以,这个问题已经解决了在我的脑海中思考了几次,我决定找到问题的根源(至少在一个实现中)。

挖掘 V8 源代码产生了 JSArray 类。

// The JSArray describes JavaScript Arrays
//  Such an array can be in one of two modes:
//    - fast, backing storage is a FixedArray and length <= elements.length();
//       Please note: push and pop can be used to grow and shrink the array.
//    - slow, backing storage is a HashTable with numbers as keys.

我假设数组元素的类型决定了它是快还是慢。我在 set_has_fast_elements (set_bit_field2(bit_field2() | (1 << kHasFastElements))) 中设置了一个位标志,这就是我想的地方当我在谷歌代码中查找并且本地没有源代码时,我会画出挖掘线。

现在,似乎任何时间任何对数组(它是 JSObject 的子类)进行操作,调用 NormalizeElements(),执行以下操作:

// Compute the effective length.
  int length = IsJSArray() ?
      Smi::cast(JSArray::cast(this)->length())->value() :
      array->length();

因此,在回答您的问题时:

  1. Chrome(或使用 V8 的其他浏览器)似乎没有任何缓存优势length 属性数组的(除非你正在做一些奇怪的事情,这会迫使它变慢(我不确定这些条件是什么)) - 话虽如此,我很可能会继续缓存 < code>length 直到我有机会了解所有操作系统浏览器实现;)
  2. length 属性似乎在any之后发生了改变> 对对象的操作

编辑:

顺便说一句,看起来一个“空”数组实际上被分配了 4 个元素:

// Number of element slots to pre-allocate for an empty array.
static const int kPreallocatedArrayElements = 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.

// The JSArray describes JavaScript Arrays
//  Such an array can be in one of two modes:
//    - fast, backing storage is a FixedArray and length <= elements.length();
//       Please note: push and pop can be used to grow and shrink the array.
//    - slow, backing storage is a HashTable with numbers as keys.

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 to NormalizeElements(), which executes the following:

// Compute the effective length.
  int length = IsJSArray() ?
      Smi::cast(JSArray::cast(this)->length())->value() :
      array->length();

So, in answering your questions:

  1. There doesn't seem to be any advantage in Chrome (or other browsers that use V8) to caching the length property of an array (unless you're doing something odd that would force it to be slow (I'm not sure what those conditions are) - having said that, I'll most likely continue to cache length until I get a chance to go through all OS browser implementations ;)
  2. The 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:

// Number of element slots to pre-allocate for an empty array.
static const int kPreallocatedArrayElements = 4;

I'm not sure how many elements the array grows by once the bounds have been exceeded - I didn't dig that deep :)

池予 2024-11-09 20:04:43

另一组性能测试。该循环是通过一个空循环对一百万个随机数的数组完成的。

在 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.

迷乱花海 2024-11-09 20:04:43

本文通过向 IRHydra 询问生成的代码来研究 V8 和 Chrome 中的自动缓存:

格林奇如何窃取 array.length 访问权限作者:维亚切斯拉夫·叶戈罗夫

他发现在某些条件下手动缓存.length实际上增加了开销而不是提高性能!

但无论如何,这种微观优化不太可能为您的用户带来任何显着的收益。为了他们的利益,也为了您的利益,请重点关注易于阅读的代码,并在代码中使用良好的数据结构和算法!

避免过早优化:专注于优雅的代码,直到出现性能问题。然后,通过分析找出瓶颈,然后优化那部分代码。

This article investigates automatic caching in V8 and Chrome, by asking IRHydra for the generated code:

How the Grinch stole array.length access by Vyacheslav Egorov

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.

作妖 2024-11-09 20:04:43

请注意:

在某些浏览器上(我在 Safari、IE 和 Opera 中注意到了这一点),您可以通过缓存 for 循环声明内的长度来提高速度:

var j;
for (var i = 0, len = arr.length; i < len; i++) {
  j = arr[i];
}

我将上面的 @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:

var j;
for (var i = 0, len = arr.length; i < len; i++) {
  j = arr[i];
}

I edited @KooiInc's jsperf test above to add this case.

一紙繁鸢 2024-11-09 20:04:43

请注意不要假设这对于所有可迭代集合都是如此。例如,缓存 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文