查看原文
其他

Facebook 开源工具 Prepack,自动优化 JavaScript 代码,提高运行速度

2017-05-04 技术风向标

今天,开源高产户 Facebook 又向开源世界贡献新品——能自动优化 JavaScript 代码的工具 Prepack,可以自动消除冗余代码,降低打包体积和执行时间。因此一举成为今天 Github 上升最快的项目。


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


来看看官方提供的优化示例,把一段段冗余累赘的代码整理得清爽干净:


Hello World


Input

(function () {  function hello() { return 'hello'; }  function world() { return 'world'; }  global.s = hello() + ' ' + world();})();


Output

(function () {  s = "hello world";})();



Elimination of abstraction tax


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的工作原理和运行机制,以下五个概念有助于更好地理解:


  • 抽象语法树(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库本身还处于早期阶段,还无法应用于生产环境,官方建议仅尝试使用,并欢迎提供反馈以帮助修复错误。


团队计划短期内稳定现有功能集,并集成 React Native 工具链,根据 React Native 所用模块系统的假设来构建优化。长期目标是把Prepack打造成一个平台,实现包括效果分析、类型分析、信息流分析、调用图推理、JavaScript 沙盒等等实用功能。


围观地址:https://github.com/facebook/prepack


近期文章

 

1.SSH默认端口为什么是22?作者Tatu Ylonen讲述背后的故事

2.有了这个神器,再也不怕看不懂复杂的shell命令了

3.从 NodeJS 文档中读出的19个套路

4.Google历时两年,发布全新Google Earth,用浏览器环游世界

5.Chrome 59支持Headless模式,PhantomJS开发者功成身退

6.Google 工程师带你入门 Headless Chrome

7.现如今学 JavaScript 是一种什么样的体验?


微信公众号"技术风向标",关注IT趋势,承载前沿、深入、有温度的内容。长按下方二维码加关注。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存