我们希望开发者可以把部分JavaScript代码替换为WebAssembly。
例如,React团队可以把虚拟DOM改用WebAssembly来实现。这样的话,使用React的开发者也不需要做任何适配,但是它们却能获得更高性能。
能够促使React团队这么做的原因最可能是WebAssembly的高性能。但是到底是什么使它有高性能呢?
在我们理解JavaScript和WebAssembly之间的性能差异原因之前,我们需要先理解JavaScript引擎所做的工作。
下图给了一个粗糙的描述,概括了当前JS应用的启动性能。
要注意的是,这些过程并不会以离散块或者特定的顺序发生。相反,它们是交叉进行的。可能会解析完一小段,就会运行一段,然后编译一段;接着解析更多代码,然后执行更多代码等等。
这种分段交叉进行的设计相比早期的JavaScript来说是一种很大的性能提升,早期的JavaScript执行更像是下图中的情形。
在最开始的时候,只有解析器来跑JavaScript,执行速度是相当慢的。当引入JIT后,执行速度得到了大幅提升。
因此,性能还是有提升空间的。
下图是与典型网页应用相比时,WebAssembly的大致过程。
不同浏览器的处理可能略有不同,下面我们以SpiderMonkey引擎为例来说明各个过程。
因为WebAssembly代码比JavaScript代码更加的精简,所以加载WebAssembly文件是更快的。尽管压缩算法能够极大减小JavaScript代码的体积,但是WebAssembly压缩后的二进制代码仍然比它要小。
JavaScript代码一旦下载到浏览器,它会被解析为抽象语法树(AST)。
浏览器通常采用的策略是惰性处理,即只解析真正被用到的代码以及只为还没被调用的函数创建存根。
而WebAssembly则不需要这种转换,因为它本身已经是一种中间代码了。它只需要经过解码,并且验证解码没有发生错误即可。
不同的浏览器编译WebAssembly时使用不同方式。一些浏览器会在运行代码前先进行基准编译,其他浏览器则会使用JIT。
但不管是哪种方式,WebAssembly都是从里机器码比较近的地方开始的。比如说,程序本身就包含了数据的类型信息,这样的话就会有更高的性能,因为:
编译器并不需要基于不同类型来编译出相同代码的不同类型版本代码
有很多优化已经在LLVM之前完成了,所以这里可以减少编译和优化的开销
有时候JIT必须丢弃之前已经优化的代码并且重新编译。
这种情况就发生在JIT之前的假设都不成立时。比如说,当循环中使用了与之前不一样的变量类型,或者原型链上新增了一个函数。
而在WebAssembly中,数据类型是很明确的,所以JIT不需要对运行时的数据类型做任何假设。也就意味着,它不不存在重新优化可能。
编写出高性能的JavaScript代码是可能的。为此,你需要知道JIT是如何做优化的。比如,你需要知道如何写出让编译器特定化数据类型的代码。
但是,大多数开发者并不知道JIT的内部实现。即便是了解JIT内部实现的人,也很难直接击中要害。许多我们为了让代码更具可读性的编程模式(比如抽出公共函数来处理多种类型)反而阻碍了编译器的优化。
而且,不同浏览器的JIT所采用的各种优化手段是不同的,这就导致了可能在某款浏览器上是最优的,但是在另一款浏览器中则是很差的。
正因为这个,运行WebAssembly通常是更加快速的。许多JIT做的优化在WebAssembly中根本不存在。
此外,WebAssembly是被设计为一个编译目标的。也就是说,它是被用来作为编译器输出的,而不是用来供开发者编码的。
因为开发者不需要直接对WebAssembly编码,所以它能够使用更适合机器的指令,而这些指令通常能做到10%~800%的性能提升。
在JavaScript中,开发者并不需要专门去清理那些不再使用的变量所占用的内存。这种清理工作由JavaScript引擎自动进行,称为垃圾回收(GarbageCollection)。
但是,如果你想要得到可预期的性能,这可能会成为阻碍。你并不能控制什么时候进行垃圾回收,所以它随时可能发生。尽管大多数浏览器在垃圾回收的调度方面做的相当不错,但是它仍然可能阻碍你的代码运行。
至少现在,WebAssembly根本不支持垃圾回收。所有内存都是手动管理的。虽然这样会让编码变得更加困难,但是它也让性能变得更加稳定。
所以,WebAssembly之所以比JavaScript拥有更好的性能,是因为以下原因:
解码WebAssembly比解析JavaScript更快
不会发生重新优化的过程,因为WebAssembly自带数据类型和其他信息
不存在垃圾回收的过程,因为它是手动管理内存的
以上就是为什么在大多数时候,WebAssembly都比JavaScript性能好的原因。
当然,WebAssembly也存在表现并不如期望的那样好的时候。同时,也有一些正在进行的改变使得它变得更快。这些我们会在下一篇中讨论。