AICon 上海站|日程100%上线,解锁Al未来! 了解详情
写点什么

Facebook 开源 JavaScript 代码优化工具 Prepack

  • 2017-05-04
  • 本文字数:2528 字

    阅读完需:约 8 分钟

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-05-04 19:004268
用户头像

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

关注

评论

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

【前端】前后端交互重点Ajaxの介绍及实战

孤寒者

json ajax tornado 6月月更

五八同城(58.com)研发效能组织和团队建设之路

laofo

互联网 DevOps 研发效能 持续交付 工程效率

流计算中的死锁

Damon

6月月更

【Spring 学习笔记(十二)】Spring AOP 切入点表达式

倔强的牛角

Java spring spring aop Java EE 6月月更

别再说你不知道分布式事务了

牧小农

C#入门系列(十八) -- 类的属性、索引、事件和this关键字

陈言必行

C# 6月月更

如何构建、部署运行Flink程序

百思不得小赵

flink 部署 6月月更

InfoQ 极客传媒15周年庆征文|云原生运维排障的关键要点

穿过生命散发芬芳

云原生 6月月更 InfoQ极客传媒15周年庆

办事利器推荐 | 社区征文

工程师日月

工具软件 6月月更 初夏征文

SDN系统方法 | 7. 叶棘网络

俞凡

架构 网络 sdn SDN系统方法

JASON

Jason199

json js math 6月月更

SeekTiger的崛起,旗下生态NFT、DAO VC平台将陆续上线

股市老人

前端食堂技术周刊第 41 期:TC 39 会议、IE 退役、React Labs、Storybook 组件百科全书

童欧巴

前端 React IE TC39

手把手教你在CentOS环境安装Docker

迷彩

Docker 架构 运维 6月月更 InfoQ极客传媒15周年庆

颠覆Web2 社交媒体,Liberty 计划在波卡上找到了归宿

One Block Community

区块链 科技

Mysql源码阅读 -- Windows10编译运行MySQL源码

c++ MySQL 源码学习

关于 Angular SSR 应用在渲染中止时如何避免内存泄漏问题的一些尝试

汪子熙

typescript angular SSR Spartacus 6月月更

Feature Store Meetup V3回顾|华为商城&第四范式&众安保险特征平台建设实践

星策开源社区

华为商城 OpenMLDB Feature Store MLOps 特征工程

Linux开发_Linux下进程编程

DS小龙哥

6月月更

模块四

Geek_2ce415

彻底搞懂 select/poll/epoll,就这篇了!

C++后台开发

网络编程 linux开发 epoll select C++开发

SeekTiger迎多重利好,旗下生态NFT、DAO VC平台将陆续上线

鳄鱼视界

远程办公三部曲 - 如何提高沟通效率| 社区征文

耳东@Erdong

沟通 远程办公 6月月更 初夏征文

spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)

程序员欣宸

Java spring SpringFramework 6月月更

算法系列之动态规划

坚果

6月月更

BOM核心——window对象之Golbal

大熊G

JavaScript 前端 6月月更

创建视图——基于函数的视图 Django

海拥(haiyong.site)

Python django 6月月更

GTID详解

乌龟哥哥

6月月更

测试需要做单元测试吗?

老张

软件测试 单元测试

Facebook开源JavaScript代码优化工具Prepack_JavaScript_刘振涛_InfoQ精选文章