初步收集的 Prepack 的信息 05-04

#1

一大早醒来被 Prepack 刷屏 https://prepack.io/

是什么?

Prepack is a tool that optimizes JavaScript source code: Computations that can be done at compile-time instead of run-time get eliminated. Prepack replaces the global code of a JavaScript bundle with equivalent code that is a simple sequence of assignments. This gets rid of most intermediate computations and object allocations.

初看想是代码压缩工具, 仔细看并不是, 是为了专门优化 js 初始化速度而设计的. 从编译结果看, 最明显的是对 JavaScript 做了简单的执行. 比如 Fabonacci 的例子, 经过优化之后直接就是结果了:

在官网能看到更多详细的例子 https://prepack.io/

背景

Prepack 的背景可以从 2016 年的一个分享看:

Sebastian McKenzie - Making Javascript Initialize Faster at react-europe 2016

具体实现上, prepack 借助了 Babel AST, 在编译过程会有个 interpreter 用来解释执行 AST, 经过解释, 编译器就知道代码具体是怎样执行的了, 就可以把对执行过程优化的结果重新返回到 js 代码, 最终得到便以结果:

At the core of Prepack is an almost ECMAScript 5 compatible interpreter — implemented in JavaScript! You can think of the interpreter in Prepack as a clean reference implementation of JavaScript.

The interpreter has the ability to track and undo all effects, including all object mutations. This enables speculative optimizations.

HN 上有一段评论值得参考 https://news.ycombinator.com/item?id=14258571

A long time ago there was a theory about using Guile (the GNU Scheme) as a general interpreter for languages using partial evaluation: you write an interpreter for a language in Scheme, use a given program as input, and run an optimizer over the program. This turns your interpreter into a compiler. I played around with the concept (making a Tcl interpreter), and it even kind of worked, often creating reasonably readable output.

Prepack looks like the same kind of optimizer – it could be a fun task to write an interpreter and see if this can turn it into a compiler/transpiler.

优势

Prepack 更像是一个编译器, 把 js 的 AST 编译到更加低级的语义. 同时根据其原理, 可以剔除掉代码当中不需要执行的代码. 最重要一点是通过预先展开代码, 可以减少 js 代码在浏览器端初始化小号的时间.

The Closure Compiler also optimizes JavaScript code. Prepack goes further by truly running the global code that initialization phase, unrolling loops and recursion. Prepack focuses on runtime performance, while the Closure Compiler emphasizes JavaScript code size.

最直观的例子就是 Fabonacci 那个, 直接把结果在编译过程就计算完成了, 省掉了浏览器计算的时间. ClojureScript 社区也有人做了对比, 可以减少初始化时脚本执行所消耗的时间:

不足

V8 团队的人在 Twitter 上有发消息, 认为 prepack 目前生成的代码没有针对引擎做良好的优化, 就算启动变快了, 在 runtime 没有 JavaScript 引擎合理的优化, 还是会有变慢的地方. prepack 并不是万能的.

另一个问题, 由于 prepack 在编译时要做一个类似于 “执行” 的操作, 然而有些数据是到真正执行才会有, 所以这个执行就需要有个 mock 数据类似的操作. 比如 DOM API 和 Node API 就需要真的 runtime, 目前需要手动告诉引擎哪些数据存在:

// Assume that a certain property has a simple known value.
__assumeDataProperty(global, "obscure", undefined);

In addition to computing over concrete values, Prepack’s interpreter has the ability to operate on abstract values which typically arise from environment interactions. For example, Date.now can return an abstract value. You can also manually inject abstract values via auxiliary helper functions such as __abstract(). Prepack tracks all operations that are performed over abstract values. When branching over abstract values, Prepack execution and explore all possibilities. Thus, Prepack implements a Symbolic Execution engine for JavaScript.

在 GitHub 是有看到用 jsdom 模拟相关 API 的计划. 等待进展.

结尾

Prepack 延续了 Facebook 把 JavaScript 当汇编的优良传统… 而且这次玩的更高端. 单单看前景的话, 还是很有意思的, 这种做法比从前的探索得更远, 也意味着可能出现更强大的优化策略. 编译器技术在其他语言已经积累了一些成熟的方案, JavaScript 社区估计有不少可以借鉴的.

目前看来项目还很难直接在项目当中使用, 特别是 DOM API 没有支持会被当做 undefined 然后报错, 问题不小. 估计还要不少时间完善. 至少在 Facebook 自家的大型项目用 prepack 优化之前, 不能随随便便用上去.

另外在 Twitter 上能挖到更多关于 prepack 的信息, 如果想看第一手的资料, 看代码, 最及时的, 看 Twitter https://twitter.com/search?src=typd&q=prepack

2 Likes
#2

今天试用了一下,吓得不敢用了

1 Like
#3

很强势,昨天才折腾了gulp,今天又双叒叕来了个prepack:joy:

#4

很强势,希望多多的补充,我们就从这里获取知识了

#5

一入前端深似海

#6

不能做伸手党 >_< 自己到 Twitter 上关注大佬去 https://twitter.com/sebmarkbage

#7

已经不能叫做前端了, 要叫 Web 平台

#8

想自己去弄第一手的资源,英语是硬伤啊!!所以还是拜托楼主

#9

感谢题老师分享