V8 JS AOT化的探索与实践
如果无法正常显示,请先停止浏览器的去广告插件。
1. 第 十 六 届 D 2 前 端 技 术 论 坛
V8 JS AOT化的探索与实践
喻世江
2. 背 景
技术选型
目录
方案设计
优化效果
展 望
3. 背 景
4. 背 景
U4内核V8引擎
快
强
JavaScript
ü Disk Code Cache
ü UC LLVM Compiler 跨平台
ü JS 卡死检测 动态化
ü OOM 定位信息
ü JS API 扩展
性能
ü JSI
稳
Native
ü 疑难崩溃攻克 Ø 运行时在线编译 Ø 打包时 PC 离线编译
ü 安全漏洞修复 Ø 每次重新编译 Ø 直接运行
5. 背 景
JS AOT
AOT:Ahead of Time,提前编译。
目标
让 JavaScript 具备动态化特性的同时,运行性能也可能与 Native 对标,尤其是首次启动。
6. 技术选型
7. 技术选型
V8 运行 JavaScript 的流水线
JavaScript
Source Code
function test(o) {
return 3 + o.x;
}
Parser
Abstract
Syntax Tree
Ignition
TurboFan
Bytecode
StackCheck
LdaSmi [3]
Star r0
LdaNamedProperty a0, [0], [1]
Add r0, [0]
Return
Optimized
Machine Code
REX.W leaq rbx,[rip+0xfffffff9]
REX.W cmpq rbx,rcx
jz 0x1efffc744 <+0x24>
REX.W movq rdx,0x3e00000
REX.W movq r10,0x7f2be00
call r10
int3l
REX.W movq rbx,[rcx-0x20]
testb [rbx+0xf],0x1
...
8. 技术选型
本地代码
(Machine Code,汇编)
全字节码缓存
(Full Code Cache)
部分字节码缓存
(Code Cache)
9. 技术选型
调研一:使用本地代码(Machine Code,汇编)
2010 年 V8 :
JavaScript
Source Code
Full-codegen
Parser
Abstract
Syntax Tree
ToString
Unoptimized
Machine Code
体积大
内存高
Crankshaft
operator +
编译慢
性能差
Optimized
Machine Code
ToNumber
CPU 架构不通用
ToPrimitive
GetMethod
GetV
ToObject
GetValue
Call
10. 技术选型
调研二:缓存全部字节码(Full Code Cache)
Ø JS 函数运行覆盖度低(43%)
350.0
运行函数个数
全字节码大小
JS源码大小
Ø 加载 & 反序列化消耗大
慢 22.7%
300.0
总函数个数
Ø 代码膨胀严重(2.6 x)
=
(越小越好)
250.0
=
全字节码缓存 VS. 部分字节码缓存
200.0
150.0
100.0
50.0
0.0
编译
运行
部分字节码缓存
总时间
全字节码缓存
首屏
11. 技术选型
调研三:缓存部分字节码(Code Cache)
Code Cache
Compile
Run
V8
Blink
ExecuteScript
Running Task
Event
Async
Compile Function
Event/Async/Timer/…
Code Cache
V8 (U4 内核) 版本的碎片化
CPU 架构通用性
机型通用性
Close
Bytecode
12. 技术选型
面临的主要难题
有效性
兼容性
13. 方案设计
14. 方案设计
保证 AOT 的
有效性
15. 方案设计
保证有效性
策略一:追求极致的性能 —— PGO (Profile-guided optimization)
仅为需要被执行的函数生成代码。
函数信息收集
(移动端预发布)
Source Code
AST
Bytecode
Run
上报
函数信息
函数信息处理
(服务端)
{
}
合并 & 简化
函数修复
运行详情
{
"s": 12, // 函数起始
"e": 26 // 函数终止
}
AOT 生成
函数信息
Source Code
AST
AOT
"name": "foo", // 函数名
"start": 12, // 函数起始
"end": 26, // 函数终止
"count": 1, // 运行次数
16. 方案设计
保证有效性
策略二:追求便捷的使用 —— 先验规则
预测需要被执行的函数。
function foo() { /* ... */ }
function bar() {
function inner() {
return (x, y) => {
return x + y;
}
}
return inner();
}
var Test = {
"a": function() { /* ... */ },
"b": function() {
return () => {
return "hello";
}
},
}
嵌套越深,使用率越低
特征
JS 越小,运行覆盖度越高
l 小型 JS:不生成
策略
l 中型 JS:全字节码缓存(覆盖度 >80%)
l 大型 JS:Top 3 层
l 运行后增量更新
17. 方案设计
保证 AOT 的
兼容性
18. 方案设计
保证兼容性
策略一:在线生成 (空闲时预热)
UI 线程
预热
打开页面
Blink 主线程
AOT 线程
JS 执行
framework1.js
framework2.js
Load
Save
AOT
UI Task
Blink Task
AOT Task
生成时机:
• APP 空闲时
• 后台线程
影响或不足:
• 资源浪费(磁盘 & CPU)
适用场景:
• 框架 JS,不经常变动
19. 方案设计
保证兼容性
策略一:在线生成 (访问时生成)
生成时机:
• 页面打开时
• 后台线程
UI 线程
打开页面
Blink 主线程
网络/解析/排版/渲染
JS 执行
预热
AOT 线程
bundle1.js bundle2.js
Save
UI Task
Blink Task
Load
AOT
AOT Task
影响或不足:
• 可能来不及
适用场景:
• 业务 JS,已经离线到本地
20. 方案设计
保证兼容性
策略二:离线生成
线下
函数信息/先验规则
Source Code
线上
离线工具
AST
内置
AOT
适用场景:
• APP冷启动时执行(没有预热时机)
• 不经常变动
• 不需要动态更新
影响或不足:
• 更新 U4 内核后,可能需同步更新 AOT
AOT
Run
l Full Code Cache
l Function.prototype.toString()
丢弃 JS 源码?
21. 方案设计
AOT 总览
移动端
Source Code
AST
Bytecode
Run
上报
预发布
服务端
函数信息
合并 & 简化
函数修复
内置/下发
函数详情
更新完善
函数信息
线上
预热
先验规则
AST
服务端离线工具
AOT
Source Code
正式
AOT
Run
22. 优化效果
23. 优化效果
正常访问 (无 AOT)
打开时预热生成 AOT (先验规则)
生成 AOT
JS 执行
JS 执行
Wall: 799.5 ms
CPU: 726.4 ms
Wall: 523.1 ms
CPU: 465.6 ms
PGO 49.0% ↑
先验规则 33.9% ↑
(TOP 30+ 站点平均)
JS 执行时间减少
35%
24. 夸克高考首屏性
能提升 17.6%
无 AOT
使用 AOT
25. 展 望
26. 展 望
U4 3.0 & 4.0
JavaScript
Source Code
Parser
Abstract
Syntax Tree
U4 5.0
Ignition
TurboFan
Bytecode
Optimized
Machine Code
TurboFan
Sparkplug
Unoptimized
Machine Code
JS 性能 ~20% ↑
27. Thanks
“U4 内核技术”公众号