写点什么

Facebook 开源 JavaScript 代码优化工具 Prepack

2017 年 5 月 04 日

5 月 4 日,Facebook 开源团队技术作者 Joel Marcey 在 Hacker News 社区发布一则《 Prepack 帮助提高 JavaScript 代码的效率》,引起了社区的广泛讨论

官方宣称 Prepack 是一个优化 JavaScript 源代码的工具,实际上它是一个 JavaScript 的部分求值器(Partial Evaluator),可在编译时执行原本在运行时的计算过程,并通过重写 JavaScript 代码来提高其执行效率。Prepack 用简单的赋值序列来等效替换 JavaScript 代码包中的全局代码,从而消除了中间计算过程以及对象分配的操作。对于重初始化的代码,Prepack 可以有效缓存 JavaScript 解析的结果,优化效果最佳。

以下五个概念可以帮助你更好地理解 Prepack 的运行机制:

  • 抽象语法树(AST)
    Prepack 运行在 AST 级别,使用 Babel 解析并生成 JavaScript 源代码。

  • 具体执行(Concrete Execution)
    Prepack 的核心是一个 JavaScript 解释器,它与 ECMAScript 5 几乎完全兼容,而且紧密地保持与 ECMAScript 2016 语言规范的一致性,你可以将 Prepack 中的解释器视为完全参照 JavaScript 实现的。

    解释器能够跟踪并撤销包括所有对象 Mutation 在内的结果,从而能够进行推测优化(Speculative Optimization)。

  • 符号执行(Symbolic Execution)
    除了对具体值进行计算外,Prepack 的解释器还可以操作受环境相互作用影响的抽象值。例如Date.now可以返回一个抽象值,你可以通过 helper 辅助函数(如__abstract())手动注入抽象值。Prepack 会跟踪所有在抽象值上执行的操作,在遇到分支时,Prepack 会执行并探索所有可能性。所以,Prepack 实现了一套 JavaScript 的符号执行引擎。

  • 抽象释义 (Abstract Interpretation)
    符号执行在遇到抽象值的分支时会分叉(fork),Prepack 会在控制流合并点加入分歧执行(Diverged Execution)来实现抽象释义的形式。连接变量和堆属性可能会得到条件抽象值,Prepack 会跟踪有关抽象值和型域(Type Domain)的信息。

  • 堆序列化(Heap Serialization)
    当全局代码返回,初始化阶段结束时,Prepack 捕获最终的堆并按顺序排列堆栈,生成直观的 JavaScript 新代码,创建并链接初始化堆中可访问的所有对象。堆中的一些值可能是抽象值的计算结果,对于这些值,Prepack 将生成原始程序完成计算所执行的代码。

以下是官方提供的 Prepack 优化示例:

复制代码
/* Hello World */
// Input
(function () {
function hello() { return 'hello'; }
function world() { return 'world'; }
global.s = hello() + ' ' + world();
})();
// Output
(function () {
s = "hello world";
})();
复制代码
/* 消除抽象税 */
// Input
(function () {
var self = this;
['A', 'B', 42].forEach(function(x) {
var name = '_' + x.toString()[0].toLowerCase();
var y = parseInt(x);
self[name] = y ? y : x;
});
})();
// Output
(function () {
_a = "A";
_b = "B";
_4 = 42;
})();
复制代码
/* 斐波那契 */
// Input
(function () {
function fibonacci(x) {
return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
}
global.x = fibonacci(23);
})();
// Output
(function () {
x = 28657;
})();
复制代码
/* 模块初始化 */
// Input
(function () {
let moduleTable = {};
function define(id, f) { moduleTable[id] = f; }
function require(id) {
let x = moduleTable[id];
return x instanceof Function ? (moduleTable[id] = x()) : x;
}
global.require = require;
define("one", function() { return 1; });
define("two", function() { return require("one") + require("one"); });
define("three", function() { return require("two") + require("one"); });
define("four", function() { return require("three") + require("one"); });
})();
three = require("three");
// Output
(function () {
function _2() {
return 3 + 1;
}
var _1 = {
one: 1,
two: 2,
three: 3,
four: _2
};
function _0(id) {
let x = _1[id];
return x instanceof Function ? _1[id] = x() : x;
}
require = _0;
three = 3;
})();
复制代码
/* 环境相互作用与分支 */
// Input
(function(){
function fib(x) { return x <= 1 ? x : fib(x - 1) + fib(x - 2); }
let x = Date.now();
if (x === 0) x = fib(10);
global.result = x;
})();
// Output
(function () {
var _0 = Date.now();
if (typeof _0 !== "number") {
throw new Error("Prepack model invariant violation");
}
result = _0 === 0 ? 55 : _0;
})();

目前 Prepack 仍处于早期开发阶段,尚未准备好在生产环境中使用,官方建议仅尝试使用,并欢迎提供反馈以帮助修复错误。

以下是 Prepack 团队对未来的规划:

  • 短期
    • 稳定现有功能集,用于预优化 (Prepack)React Native 代码包
    • 集成 React Native 工具链
    • 根据 React Native 所用模块系统的假设来构建优化
  • 中期
    • 进一步优化序列化(Serialization),包括
      • 消除不暴露特性(identity)的对象
      • 消除未使用的导出属性
    • 预优化每个函数、基本代码块、语句、表达式
    • 与 ES6 保持完全一致
    • 支持广泛的模块系统
    • 假设 ES6 支持某些功能,延迟完成或直接忽略 Polyfill 应用
    • 进一步实现 Web 和 Node.js 环境中的兼容性目标
    • 深入集成 JavaScript 虚拟机,改进堆反序列化过程,包括
      • 暴露“对象懒初始化”的概念 - 以一种 JavaScript 无感知的方式,在首次使用对象时对其进行初始化
      • 通过专门的字节码提高普通对象创建的编码效率
      • 将代码分为两个阶段:1) 非环境依赖阶段,虚拟机可以安全地捕获并恢复生成的堆;2) 环境依赖阶段,通过从环境中获得的值执行所有剩余的计算过程来拼凑具体的堆
    • 总结循环和递归
  • 长期 - 利用 Prepack 作为一个平台
    • JavaScript Playground - 通过调整 JavaScript 引擎体验 JavaScript 特性,这些引擎由 JavaScript 所编写,托管在浏览器中;你可以把它想象成一个“Babel 虚拟机”,实现了不能被编译的 JavaScript 新特性
    • 捉 Bug - 发现异常崩溃、执行问题……
    • 效果分析,例如检测模块工厂函数可能的副作用或强制纯净注释
    • 类型分析
    • 信息流分析
    • 调用图推理,允许内联和代码索引
    • 自动测试生成,利用符号执行的特性与约束求解器(Constraint Solver)结合来计算执行不同执行路径的输入
    • 智能模糊 (Smart Fuzzing)
    • JavaScript 沙盒 - 以不可观察的方式有效地测试 JavaScript 代码
2017 年 5 月 04 日 19:003156
用户头像

发布了 63 篇内容, 共 125.6 次阅读, 收获喜欢 34 次。

关注

评论

发布
暂无评论
发现更多内容

架构师 3 期 3 班 -week9- 总结

zbest

总结 week9

如何开发一个完善的Kafka生产者客户端?

码农架构

kafka 中间件 消息中间件 架构·

区块链数字货币钱包系统软件开发|区块链数字货币钱包APP开发

开發I852946OIIO

系统开发

2020 总结 | VoltDB的亮点,你了解多少?

VoltDB

数据库 物联网 VoltDB

史上最全!阿里巴巴2021年最新最全500道Java后端面试大全(值得收藏)

云流

Java 编程 程序员 面试

【高并发】ReadWriteLock怎么和缓存扯上关系了?!

冰河

并发编程 读写锁 高并发 性能调优 ReadWriteLock

还热乎的面经

书旅

百度 面试 面经

FFMpeg解码API以及在解码过程中存在的丢帧问题

wangwei1237

ffmpeg 视频解码

架构师 3 期 3 班 -week9- 作业

zbest

作业 week9

迄今为止把Mybatis讲解的最详细的PDF,图文并茂,通俗易懂

Java成神之路

Java 程序员 架构 面试 编程语言

HTML(六)——html表单

程序员的时光

程序员 前端 七日更 28天写作

深度 | 阿里云蒋江伟:什么是真正的云原生?

阿里巴巴云原生

云计算 容器 运维 云原生 k8s

从零开始学java第一天(为报训练营做准备)

落曦

人设崩塌的美国生物实验室

脑极体

有人相爱,有人年少财务自由,有人数据结构都背不出来

Java架构师迁哥

花了10000小时从外包到大厂,鬼知道我经历了什么!但回头看来这一路辛酸还是值得的!

程序员小毕

Java 面试 分布式 微服务 算法

朋友不讲武德急催我给他Java干货教程,我劝他耗子尾汁并丢给他一份GitHub上标星115k+的Java教程,他看了之后连忙向我道歉!

Java成神之路

Java 程序员 架构 面试 编程语言

疫情闭关修炼半个月,我竟把JDK源码都读懂了!

996小迁

Java 编程 架构 面试 程序人生

你跟涨薪只差这份Java核心知识点文档,读懂它你就是技术大佬!

Java架构之路

Java 程序员 架构 面试 编程语言

AQS之ReentrantReadWriteLock写锁

伯阳

读写锁 ReentrantReadWriteLock 多线程与高并发 lock

知乎获赞5K+的面试总结,蚂蚁高级工程师的技术笔记,共12W字

Java成神之路

Java 程序员 架构 面试 编程语言

阿里大佬整理的526页Netty笔记,面试大厂不再被网络通信难倒

Java成神之路

Java 程序员 架构 面试 编程语言

对微服务架构设计实践中若干问题的探讨

xcbeyond

微服务 微服务架构 微服务网关 28天写作

DCache 分布式存储系统|安装部署与应用创建

TARS基金会

缓存 分布式 微服务 存储 TARS

OMG,阿里大佬Kafka手写实战记录,这也太香了

Java架构之路

Java 程序员 架构 面试 编程语言

Java虚拟机知识 - JVM入门

小马哥

Java JVM 架构师 Java虚拟机 七日更

week9-homework

J

k8s 上运行我们的 springboot 服务之——springboot服务https请求

柠檬

Java k8s https Istio,

厉害!腾讯T3-2都还在学的微服务+MySQL+Kafka+boot2.x+虚拟机PDF

Java架构之路

Java 程序员 架构 面试 编程语言

惊悚,单个java进程占用700%的CPU

万里无云

Java 后端 cpu

限时!字节Java程序性能优化宝典开源,原来这才叫性能优化

互联网架构师小马

Java 性能优化

Facebook开源JavaScript代码优化工具Prepack-InfoQ