转载

Facebook开源JavaScript代码优化工具Prepack

  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)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 代码

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

正文到此结束
Loading...