javascript - 如何解释 js-array 循环中无法执行的性能?
问题描述
例如,假设我们有一个包含 200 000 个元素的数组......现在我们想以不同的方式迭代它并检查最快的那个。我听说如果我们在循环之前将 array.length 保存在变量中,我们将减少执行时间,所以我尝试了下面的代码
let sum = 0
for (let i = 0; i < arr.length; ++i) sum += arr[i]
反对
let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) sum += arr[i]
但是我得到了相同的结果,就好像在这两种情况下,js 一开始只读取一次长度值。
然后我决定检查,如果在循环期间我们将更改一个数组,删除最后一个元素。
let sum = 0
for (let i = 0; i < arr.length; ++i) {
sum += arr[i]
if (i === 100) arr.pop()
}
反对
let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
sum += arr[i]
if (i === 100) arr.pop()
}
所以我希望第二种情况现在应该更快地工作,因为在第一种情况下,js 不可避免地应该每次检查 array.length,我很惊讶它的工作速度不是更快,而是更慢 - 从 10% 到 15%。对我来说这是无法解释的。有任何想法吗?
解决方案
问题是
let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
sum += arr[i]
if (i === 100) arr.pop()
}
现在是不正确的,因为它循环超出了数组的末尾。终会sum
如此NaN
。正确的解决方案将l = arr.length - 1
不会发生这种情况。
现在为什么它变得这么慢?因为只有当元素存在时,javascript 中的数组访问才快速(编译为带有指针寻址的快速路径)。当您错过时,代码将被取消优化。由于 JSbench 多次运行相同的代码,因此即使去优化仅发生在 200000 次迭代中的最后一次,后续运行也会慢得多。
有关详细信息,请参阅此演讲,其中甚至在一张幻灯片上明确说明了“避免越界读取” 。一般来说,不要使用非惯用代码来提高性能。i < arr.length
是惯用的,JS引擎会很好地优化它。
推荐阅读
- javascript - 使用 JS 和 SCSS 的导航动画,你能告诉我哪里出错了吗?
- flask - jinja2.exceptions.UndefinedError: 'flask.ctx._AppCtxGlobals object' 没有属性 'words'
- android - com.google.android.support:wearable:2.4.0 和 AndroidX 迁移
- python - 如何通过按 1 键制作移动矩形?
- webpack - 如何使用散列名称命名 css 文件,就像 js 文件一样?
- javascript - 如何使排列代码更简洁?
- java - Android 应用程序尝试加载链接在我的库文件中的 .so
- python - Maya workspaceControl 'uiScript' 未更新为新功能
- jenkins - 带有 list-git-branches-parameter-plugin 的 Jenkinsfile
- python - 将 CSV 转换为邻接列表