5

wean,比taro快100倍的小程序编译器

 3 years ago
source link: https://zhuanlan.zhihu.com/p/385343070
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

wean,比taro快100倍的小程序编译器

✅趴在床上娇喘,❎隔着网线叫唤

午休写一篇水文……大家是不是已经把 wean 给忘了-_-||

最近重构了 wean,现在 wean 真的超级快,编译同一个小程序项目,taro 需要大约 20s 时间,wean 只需要 200 ms

啊这,这跨越100倍的性能提升,到底有什么秘密?

  1. esbuild

wean 使用 esbuild 做 js 的解析和打包,这块是最无痛的性能优化了,大约可以提升十倍的性能

但是在实际使用过程中,esbuild 有很多地方比较鸡肋,大家可以参考——

① 不支持 AST 操作    ② 不支持生成 d.ts

所以如果你需要大量操作 AST,比如 taro,则只能和 parcel 一样选择使用 swc,swc 也很快,虽然比 esbuild 慢点,但比 babel 快得多

如果你需要快速生成 d.ts,那无解,用 tsc 太慢了,还是自己手写吧

wean 经过精心的设计和安排,完全没有 AST 的操作,所以使用 esbuild 是最合适的

2. 算法复杂度

写编译器,数据结构和算法复杂度的安排尤为重要,esbuild 之所以快,除了 go 天生,他的算法安排也很重要

我们安排编译器的基本原则是能 O(n) 就 O(n),最好能做到 O(1),千万不要嵌套循环,不要让复杂度呈指数级上涨

举几个例子:

① 比如我们每个文件都走一遍 esbuild,那么这个复杂度就是 O(mn),m 是文件数量,n 是 js 节点数量,但是我们先做文件的打包,这只是一层字符串级别的切割和拼接,最终只走一次 esbuild,那么复杂度就是 O(n) 了

② 本来我们走全量编译,需要 O(n) 的复杂度,但是我们做按需编译,一个页面只依赖一个文件,那么这个复杂度就变成了了 O(1),vite 巧妙地利用了浏览器文件系统做到了这一点

③ 递归生成和拷贝文件,这个 IO 操作的性能也是很差的,但是我们不走递归,最终生成一个扁平的文件目录和一个 mapping 表,性能也会好很多

上面举例子都是些算法和数据结构的安排,但是这块是提升性能最明显的

taro 之所以慢,是因为在操作 AST 的过程中,循环嵌套循环,使得复杂度呈指数级别增长,这样很容易变得超级慢

3. 自研编译器

有的时候,可能大部分情况代码是良好的,但是偏偏有一个插件,一个模块,为了扩展它,手动做了很多次遍历,就变慢了

我自己写了 wxml-parser,和 esbuild 一样,在扩展 case 的过程中,它整体的复杂度维持在 O(n),虽然比不上 go 的指标,但也至少不慢

4. 总结

最近好多团队在重构他们的编译器,每次看到这些工具链级别的性能提升,真的很赞

比如 umi 3.5 ,借助 module federation 提速十倍

Release v3.5.0 · umijs/umi

比如 parcel 借助 swc 提速十倍

Parcel 2 beta 3

其实 wean 还有很多东西没做,比如 node worker,增量编译,按需编译,长效缓存……

因为小程序比较特殊,它不是跑在浏览器中,而是 jscore 和 webview 中,编译器后端做的事情不再是辅助编译了,所以很多 feature 我都延期了

但是 wean 现在已经够快了,事实上只要写代码的时候仔细安排一下数据结构和算法,就不会特别慢

ctripcorp/wean

最后放一个地址,欢迎 star,我还会借助 wean 继续研究编译方面的东西


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK