写点什么

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:004033
用户头像

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

关注

评论

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

Bitmap、RoaringBitmap原理分析

京东科技开发者

数据结构 算法 存储 BitMap 企业号 3 月 PK 榜

Error:SyntaxError:JSON Parse error:Unexpected EOF 解决办法

Rose

adobe 安装报错

Movist Pro for mac播放流畅、观影愉悦!

Rose

苹果电脑 视频播放器 Movist Pro Mac Movist Pro 中文版

GTC 2023的算力协奏曲,NVIDIA与宁畅“共舞”AI时代

脑极体

算力

远程桌面连接是什么?远程桌面连接使用教程

RayLink远程工具

如何针对多租户 SaaS 使用案例扩展机器学习推理

亚马逊云科技 (Amazon Web Services)

Amazon SageMaker

Docker 开始清退开源组织,不付费就删除所有私镜像怎么看

HoneyMoose

尤大:不会说 Rap 的前端不是好前端!写一个 v-rap 指令!

泰罗凹凸曼

JavaScript 有趣的技术知识

Acrobat DC弹窗:“未找到IMSlib,Acrobat服务将无法正常工作”,如何解决?

Rose

Acrobat DC 服务将无法正常工作

免费的远程控制软件有哪些?远程控制软件哪个好用?

RayLink远程工具

远程控制软件 远控软件

远程桌面和远程协助的区别,选择哪个方式连接更好?

RayLink远程工具

远程协助 远程桌面连接

中国量子产业集结,用知识产权助力量子应用化

脑极体

2万字60道MySQL经典面试题总结(附答案)

程序员大彬

MySQL 数据库 java面试

远程控制软件RayLink助力畅玩爆火双人游戏

RayLink远程工具

远程控制软件

远程桌面连接如何设置?远程桌面连接的设置方法

RayLink远程工具

远程桌面连接

Nautilus Chain主网上线在即空投规则公布,如何获得更多的空投?

鳄鱼视界

ChatGPT编程秀-2:最小元素的设计

仝键

JavaScript 编程 架构 java ChatGPT

远程桌面连接有哪些方式?远程桌面工具推荐

RayLink远程工具

远程桌面连接 远程桌面工具

压测模式该怎么选?RunnerGo五大压测模式详解

爱研究代码的极客人

Jmeter 性能测试 自动化测试 压力测试 LoadRunner

好用的远程控制桌面连接工具有哪些?

RayLink远程工具

远程桌面连接

DrawerLayout(官方侧滑菜单)的简单使用

梦笔生花

ViewPager DrawerLayout SlidingMenu

火山引擎边缘渲染,驱动游戏体验升级

火山引擎边缘云

边缘计算 火山引擎 渲染 边缘云

如何将「知识」体系化管理

Java 架构 职场 知识管理

远程控制软件如何实现两台电脑连接

RayLink远程工具

远程控制软件

华为全新发布2023春季旗舰新品,智慧生活再进化

最新动态

给程序加个进度条吧!1行Python代码,快速搞定~

程序员晚枫

Python pip 进度条

电脑怎么远程连接服务器?如何进行远程桌面连接?

RayLink远程工具

远程桌面连接

内网渗透之某后渗透利用

网络安全学海

黑客 网络安全 安全 信息安全 渗透测试

数据库+chatGPT3.5 优化、索引、注释、SQL就是一句话的事了

非喵鱼

Java 数据库 openai ChatGPT ChatGPT4

Nautilus Chain主网上线在即空投规则公布,如何获得更多的空投?

股市老人

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