这个月不仅是谷歌 Chrome 的十岁生日,也是 V8 的十周年纪念日。这篇文章讲述了 V8 在过去 10 年中经历的主要里程碑,以及在它诞生之前的那些秘密的岁月。
视频:使用 gource 创建的 V8 代码库可视化演化进程,相当精彩( https://youtu.be/G0vnrPTuxZA )。
V8 诞生之前的秘密岁月
2006 年秋天,谷歌聘请 Lars Bak 为 Chrome 浏览器构建一个新的 JavaScript 引擎,当时它还只是谷歌内部的一个秘密项目。后来,Lars 从硅谷回到了丹麦的奥胡斯。Lars 想留在丹麦,但那里没有谷歌办事处,于是 Lars 和几个最初参与该项目的工程师开始在他的农场办公。新的 JavaScript 运行时被命名为“V8”,灵感源自 50 年代经典的“肌肉车”的引擎。后来,随着 V8 团队不断成长,开发者从农场搬到了奥胡斯的一个现代化的办公大楼里。然后,整个团队开始专注于构建地球上最快的 JavaScript 运行时。
V8 项目的启动和演化
2008 年 9 月 2 日,V8 与 Chrome 在同一天宣布开源。最初的代码提交日期可追溯到 2008 年 6 月 30 日。在那之前,V8 是在一个私有 CVS 存储库上开发的。最初,V8 只支持 ia32 和 ARM 指令集,并使用 SCons 作为构建系统。
2009 年,V8 引入一个名为 Irregexp 的正则表达式引擎,改进了真实世界的正则表达式性能。随着 x64 移植的引入,支持的指令集数量从两个增加到三个。2009 年,内嵌 V8 的 Node.js 发布了第一个版本。在最初的 Chrome 漫画中明确提到了将 V8 嵌入到非浏览器项目中的可能性,而 Node.js 做到了!Node.js 成为最受欢迎的 JavaScript 生态系统之一。
2010 年,V8 引入了全新的优化 JIT 编译器 Crankshaft,从而极大提升了运行时性能。Crankshaft 生成的机器代码比之前的 V8 编译器(未命名的)快两倍,而体积小了 30%。同年,V8 增加了第四个指令集:32 位 MIPS。
2011 年,垃圾回收器性能得到了极大的改善。新的增量垃圾回收器大大减少了停顿时间,同时保持了极佳的峰值性能和低内存使用率。V8 引入了隔离的概念,可以在一个进程中启动多个 V8 运行时实例,为在 Chrome 中实现轻量级的 Web Worker 铺平了道路。后来我们从 SCons 转向 GYP,这是第一次进行 V8 构建系统迁移。我们实现了对 ES5 strict mode 的支持。与此同时,开发工作从奥胡斯移交到了德国慕尼黑。
2012 年是 V8 项目的基准测试年。团队通过不断的速度冲刺迭代来优化 V8 的性能,并使用 Sunspider 和 Kraken 基准套件来测量性能。后来,我们自己开发了一个名为 Octane 的基准测试套件(其核心是 V8 Bench),它引领了峰值性能竞赛,促进了所有主要 JS 引擎的运行时和 JIT 技术的大幅改进。所有这些努力导致的一个结果是从随机抽样转向了一种基于确定性和计数的技术,用于检测 V8 运行时分析器中的“热”函数。
2013 年,一个名为 asm.js 的 JavaScript 子集出现了。由于 asm.js 只支持静态类型的算术运算、函数调用和基本类型的堆访问,因此 asm.js 代码的运行性能是可预测的。我们发布了新版 Octane(Octane 2.0),对现有基准测试进行了更新,并针对 asm.js 等用例增加新的基准测试。Octane 促进了编译器优化的发展,例如分配折叠和用于类型转换和预定的基于分配站点的优化,大大提高了峰值性能。作为内部“Handlepocalypse”计划的一部分,我们对 V8 Handle API 进行了彻底重写,提升其易用性和安全性。同年,Chrome 实现的 TypedArrays 从 Blink 转到了 V8 中。
2014 年,V8 通过并发编译将 JIT 编译的一些工作从主线程中移除,以此来减少堵塞,并显著提升了性能。后来,我们推出了名为 TurboFan 的新优化编译器的初始版本。同时,我们的合作伙伴将 V8 移植到三个新的指令集架构上:PPC、MIPS64 和 ARM64。继 Chromium 之后,V8 转向了另一个构建系统 GN。V8 的测试基础设施得到了显著改进,可以使用 Tryserver 在构建开始之前测试每个补丁。在源代码控制方面,V8 从 SVN 迁移到了 Git。
2015 年是 V8 最繁忙一年。我们实现了代码缓存和脚本流,大大加快了网页加载速度。那年晚些时候,我们开始了新解释器 Ignition 的开发工作。我们尝试了 strong mode,打算让它成为 JavaScript 的子集,以实现更强和更可预测的性能保证。我们实现了 strong mode,但后来发现它所带来的好处并不足以抵消我们所花费的成本。而我们添加的提交队列却大大提高了生产力和稳定性。V8 的垃圾回收器也开始与 Blink 等嵌入器合作,以便在空闲时进行垃圾回收。空闲时垃圾回收显著减少了垃圾回收停顿和内存消耗。当年的 12 月分,第一个 WebAssembly 原型来到了 V8 上。
2016 年,我们发布了最后一组 ES2015(以前称为“ES6”)特性集(包括 promise、类语法、词法作用域、解构等),以及一些 ES2016 特性。我们还开始推出新的 Ignition 和 TurboFan 管道,用它来编译和优化 ES2015 和 ES2016 特性,并将 Ignition 作为低端 Android 设备的默认配置。我们在 PLDI 2016 大会上展示了我们的空闲时垃圾回收工作成功。我们启动了 Orinoco 项目,这是一个针对 V8 的并发垃圾回收器,旨在减少主线程垃圾回收时间。我们将性能工作从合成微基准转向了测量和优化实际性能。出于调试的目的,V8 检查器从 Chromium 迁移到了 V8,允许任何 V8 嵌入器(不仅仅是 Chromium)使用 Chrome DevTools 来调试在 V8 中运行的 JavaScript。WebAssembly 从原型转为了实验支持。V8 获得了 ACM SIGPLAN 编程语言软件大奖。在这一年还新增了另一个移植平台:S390。
2017 年,我们终于完成了对 V8 引擎进行的重大修整,默认情况下启用新的 Ignition 和 TurboFan 管道。这样就有可能在后续从代码库中移除 Crankshaft(130,380 个已删除的代码行)和 Full-codegen。我们推出了 Orinoco v1.0,包括并发标记、并发扫描、并行清理和并行压缩。我们正式将 Node.js 视为 V8 的一等嵌入器。从那以后,如果某些 V8 补丁无法通过 Node.js 测试套件的测试,就不能推出这些补丁。我们的基础设施提供了正确模糊测试支持,确保任何代码都能产生一致的结果,无论是在怎样的配置下运行。
在一个面向整个业界的协调启动发布中,V8 默认启用了 WebAssembly。我们实现了对 JavaScript 模块以及 ES2017 和 ES2018 完整特性集的支持(包括异步函数、共享内存、异步迭代,rest/spread 属性和 RegExp)。我们提供了对 JavaScript 代码覆盖的原生支持,并启动了 Web Tooling Benchmark,用以帮助我们衡量 V8 的优化对实际开发者工具以及这些工具所生成的 JavaScript 输出的性能的影响。我们可以借助用于跟踪 JavaScript 对象到 C++ DOM 对象的包装器来解决 Chrome 中长期存在的内存泄漏问题,并有效地处理 JavaScript 和 Blink 堆上对象的闭包传递。我们后来使用这个基础设施来提升开发者工具的堆快照能力。
2018 年,一个行业范围的安全事件(Spectre/Meltdown 漏洞)颠覆了我们对 CPU 信息安全的认知。V8 工程师进行了大量有关安全攻击问题的研究,以便更好地理解托管语言所面临的威胁并提出解决措施。V8 为运行不受信任代码的嵌入器提供了针对 Specter 和类似侧通道攻击的缓解措施。
最近,我们为 WebAssembly 发布了一个名为 Liftoff 的基线编译器,它大大减少了 WebAssembly 应用程序的启动时间,同时提供了可预测的性能。我们发布了 BigInt,一个新的 JavaScript 原始类型,可以实现任意精度的整数。我们实现了嵌入式内置函数,并可以对它们进行惰性反序列化,从而显著降低 V8 多个隔离的占用空间。现在可以让后台线程编译脚本字节码。我们启动了 Unified V8-Blink Heap 项目,可同步运行跨 V8 和 Blink 的垃圾回收。
性能的起伏
多年来,Chrome 的 V8 Bench 分数显示出 V8 的变化对性能产生了巨大影响。(我们正在使用 V8 Bench,因为它是仍然可以在最初 Chrome 测试版中运行的少数基准测试之一。)
从 2008 年到 2018 年的 Chrome V8 Bench 分数
在过去十年中,我们在这个基准测试中的分数提升了 4 倍!
不过,你可能也注意到其中有两次出现性能下降,它们分别对应于 V8 历史上的两个重大事件。2015 年的性能下降发生在 V8 发布 ES2015 特性基准版的时候,我们更多地关注特性的正确性,而忽略了初始版本的性能。我们以微小的性能回退换来能够尽快让开发者用上这些特性。在 2018 年初,因为 Spectre 漏洞,V8 发布了缓解措施以保护用户免受潜在攻击,导致性能再次下降。所幸的是,现在 Chrome 正在发布了站点隔离,我们可以禁用这些缓解措施,从而恢复性能。
从图中我们还可以看出,V8 的性能从 2013 年左右开始趋于平稳。这是否意味着 V8 放弃并停止在性能方面继续投入?恰恰相反!图中所示的平稳说明了 V8 团队从合成微基准(如 V8 Bench 和 Octane)转向了优化实际性能。V8 Bench 是一个旧的基准测试,它不使用任何现代 JavaScript 功能,与实际的生产代码也有一定的距离。下面是使用更新的 Speedometer 基准套件得出的结果:
从 2013 年到 2018 年 Chrome 的 Speedometer 1 得分
从 2013 年到 2018 年,V8 Bench 显示的改进程度微乎其微,但在同一时期,Speedometer 1 得分上升了 4 倍。(我们使用了 Speedometer 1,因为 Speedometer 2 使用了 2013 年还不支持的现代 JavaScript 特性。)
如今,我们有更好的基准测试,可以更准确地反映现代 JavaScript 应用程序。最重要的是,我们会主动测量和优化现有的 Web 应用程序。
总结
尽管 V8 最初是为谷歌 Chrome 而构建的,但它一直是一个独立的项目,它有自己的代码库,并提供了嵌入式 API,让其他程序可以使用它的 JavaScript 执行服务。在过去的十年中,这个项目的开放性不仅让它成为 Web 平台的关键技术,也成为其他环境(如 Node.js)的关键技术。Web 的故事可以说是源远流长,庆祝 Chrome 和 V8 的 10 岁生日是一个重要的里程碑,但 Web 平台的故事已经延续了 25 年。毫无疑问,Web 的故事会继续传承下去。我们希望 V8、JavaScript 和 WebAssembly 在后续的 Web 故事中继续扮演有趣的角色。同时,我们也很期待未来十年将会发生些什么!
英文原文: https://v8project.blogspot.com/2018/09/10-years.html
感谢覃云对本文的审校。
评论 1 条评论