V8 引擎新特性——静态根:具有编译时常量地址的对象

你有没有想过 undefinedtrue 和其他核心 JavaScript 对象从何而来?这些对象是任何用户定义的对象的原子,需要首先存在。V8 称它们为不可移动的不可变根,它们生活在自己的堆中------只读堆。由于它们经常被使用,因此快速访问至关重要。还有什么比在编译时正确猜测他们的内存地址更快的呢?

例如,考虑极其常见的 IsUndefined API 函数。如果我们可以简单地检查一个对象的指针是否以 0x61 结尾,而不是查找 undefined 对象的地址以供参考,那会怎样?这正是 V8 的静态根功能所实现的。这篇文章探讨了我们实现此目标所必须克服的障碍。该功能已经登陆 Chrome 111 并为整个虚拟机带来了性能优势,特别是加速了 C++ 代码和内置函数的执行。

引导只读堆

创建只读对象需要一些时间,因此 V8 会在编译时创建它们。要编译 V8,首先编译一个名为 mksnapshot 的最小原型 V8 二进制文件。它创建所有共享的只读对象以及内置函数的本机代码,并将它们写入快照。然后,编译实际的 V8 二进制文件并将其与快照捆绑在一起。为了启动 V8,快照被加载到内存中,我们可以立即开始使用它的内容。下图显示了独立 d8 二进制文件的简化生成过程。

一旦 d8 启动并运行,所有只读对象在内存中都有固定的位置,并且永远不会移动。当我们 JIT(Just-In-Time)代码时,例如,我们可以直接引用undefined代码。但是,在构建快照和编译 libv8 的 C++ 时,地址尚不清楚。它取决于构建时未知的两件事:首先是只读堆的二进制布局,其次是只读堆在内存空间中的位置。

如何预测地址?

V8 使用指针压缩。我们不是完整的 64 位地址,而是以 32 位偏移量将对象引用到 4GB 内存区域。对于许多操作(如属性加载或比较),唯一标识对象只需要 32 位偏移到该笼中即可。因此,我们的第二个问题------不知道只读堆在内存空间中的什么位置------实际上并不是一个问题。我们只需将只读堆放在每个指针压缩笼的开头,从而为其提供已知位置。例如,对于 V8 堆中的所有对象,undefined 始终具有最小的压缩地址,从 0x61 字节开始。这就是我们如何知道,如果任何 JS 对象的完整地址的低 32 位是0x61,那么它一定是undefined

这已经很有用了,但我们希望能够在快照和 libv8 中使用这个地址------一个看似循环的问题。但是,如果我们确保 mksnapshot 确定性地创建一个完全相同的只读堆,那么我们可以在构建之间重用这些地址。为了在 libv8 中使用它们,我们基本上构建了两次 V8:

第一次调用mksnapshot时,唯一生成的产物是一个文件,其中包含只读堆中每个对象相对于cage基址的地址。在构建的第二阶段,我们再次编译 libv8,并有一个标志确保每当我们引用 undefined 时,我们实际上使用 cage_base + StaticRoot::kUndefined;当然,会在 static-roots.h 文件中定义undefined 的静态偏移量。在许多情况下,这将允许创建 libv8 的 C++ 编译器和 mksnapshot 中的内置编译器创建更高效的代码,因为另一种方法是始终从根对象的全局数组加载地址。我们最终得到一个 d8 二进制文件,其中 undefined 的压缩地址被硬编码为0x61

好吧,从传统上讲,这就是一切的运作方式,但实际上我们只构建一次 V8------不是没有人有时间做这件事。生成的 static-roots.h 文件缓存在源存储库中,并且只有在我们更改只读堆的分布时才需要重新创建。

未来应用

说到实用性,静态根可以实现更多的优化。例如,我们已经将公共对象分组在一起,允许我们实现一些操作作为对其地址的范围检查。例如,所有字符串映射(即描述不同字符串类型布局的隐藏类(hidden-class)元对象)都是彼此相邻的,因此,如果对象的映射具有介0xdd0x49d 之间的压缩地址,则该对象是字符串。或者,真实对象必须具有至少0xc1的地址。

并非一切都与 V8 中 JITed 代码的性能有关。正如这个项目所展示的,对 C++ 代码进行相对较小的更改也会产生重大影响。例如,Speedometer 2 是一个执行 V8 API 以及 V8 与其嵌入器之间交互的基准测试,由于静态根,它在 M1 CPU 上的得分提高了约 1%。

参考翻译

相关推荐
拳打南山敬老院34 分钟前
Context 不是压缩出来的,而是设计出来的
前端·后端·aigc
用户30767528112738 分钟前
💡 从"傻等"到"流淌":我在AI项目中实现流式输出的血泪史(附真实代码+深度解析)
前端
bluceli39 分钟前
前端性能优化实战指南:让你的网页飞起来
前端·性能优化
SuperEugene41 分钟前
Vue状态管理扫盲篇:如何设计一个合理的全局状态树 | 用户、权限、字典、布局配置
前端·vue.js·面试
没想好d42 分钟前
通用管理后台组件库-9-高级表格组件
前端
阿虎儿1 小时前
React Hook 入门指南
前端·react.js
核以解忧1 小时前
借助VTable Skill实现10W+数据渲染
前端
WangHappy1 小时前
不写 Canvas 也能搞定!小程序图片导出的 WebView 通信方案
前端·微信小程序
李剑一1 小时前
要闹哪样?又出现了一款新的格式化插件,尤雨溪力荐,速度提升了惊人的45倍!
前端·vue.js
闲云一鹤1 小时前
Git LFS 扫盲教程 - 你不会还在用 Git 管理大文件吧?
前端·git·前端工程化