WebAssembly原理与核心技术
上QQ阅读APP看书,第一时间看更新

1.1 Wasm简史

和很多其他项目(比如Go和Rust语言)一样,Wasm也起源于一个业余时间项目(Part-time Project)。2010年,Alon Zakai放弃了自己的创业公司,加入Mozilla从事Android Firefox开发相关工作。此时的Alon想把他以前开发的游戏引擎移植到浏览器上运行,他认为JavaScript的执行速度已经足够快了,所以开始在业余时间编写编译器,把C++代码(通过LLVM IR)编译成JavaScript代码。这个业余时间项目就是Emscripten。

到了2011年底,Emscripten项目已经取得了很大进展,甚至能够成功编译Python和Doom等大型C++项目。Mozilla觉得这个项目很有前途,于是成立研究团队,邀请Alon加入并全职开发Emscripten。如前文所述,由于JavaScript语言太灵活了,JIT编译器很难再做一些激进的优化(例如类型转化)。为了帮助JIT编译器做这些优化,Alon和Luke Wagner、David Herman等人一起,在2013年提出了asm.js[1]规范。asm.js是JavaScript语言的一个严格子集,试图通过减少动态特性和添加类型提示的方式帮助浏览器提升JavaScript优化空间。相较于完整的JavaScript语言,裁剪后的asm.js更靠近底层,更适合作为编译器目标语言。下面是一个用asm.js编写的例子。


function MyAsmModule() {
    "use asm"; // 告诉浏览器这是一个asm.js模块
    function add(x, y) {
        x = x|0;        // x是整数
        y = y|0;        // y也是整数
        return (x+y)|0; // 返回值也是整数
    }
    return { add: add };
}

从上面这个例子不难看出,asm.js有优点也有缺点。优点非常明显:asm.js代码就是JavaScript代码,因此完全可以跨浏览器运行。能识别特殊标记的“聪明”浏览器可以根据提示进行激进的JIT优化,甚至是AOT编译,大幅提升性能。不能识别特殊标记的“笨”浏览器也可以忽略这些提示,直接按普通JavaScript代码来执行。asm.js的缺点也很明显,那就是“底层”得不够彻底,例如代码仍然是文本格式;代码编写仍然受JavaScript语法限制;浏览器仍然需要完成解析脚本、解释执行、收集性能指标、JIT编译等一系列步骤。如果采用像Java类文件那样的二进制格式,不仅能缩小文件体积,减少网络传输时间和解析时间,还能选用更接近机器的字节码,这样AOT/JIT编译器实现起来会更轻松,效果也更好。

差不多在Alon和Mozilla开发Emscripten/asm.js的同时,Google的Chrome团队也在试图解决JavaScript性能问题,但方向有所不同。Chrome给出的解决方案是NaCl(Google Native Client)和PNaCl(Portable NaCl)。通过NaCl/PNaC1,Chrome浏览器可以在沙箱环境中直接执行本地代码。asm.js和NaCl/PNaC1技术各有优缺点,二者可以取长补短。Mozilla和Google也看到了这一点,所以从2013年开始,两个团队就经常交流和合作。

在交流过程中,Mozilla和Google决定结合两个项目的长处,合作开发一种基于字节码的技术。到了2015年4月,这一想法已经很成熟了,“WebAssembly”也取代其他临时名称,逐渐出现在两个团队的沟通邮件中。2015年7月,Wasm正式开始设计并对外公开开发速度。同年,W3C成立了Wasm社区小组(成员包括Chrome、Edge、Firefox和WebKit),致力于推动Wasm技术的发展。

2017年2月底,Wasm社区小组达成共识,Wasm的MVP(Minimum Viable Product)设计基本定稿。一个月后,Google决定放弃PNaCl技术,推荐使用Wasm。Mozilla也基本放弃了asm.js技术,甚至可能会在未来停止Emscripten对asm.js的支持,仅支持Wasm。截至本书完稿,Wasm规范已经发布了1.1版,虽然还在修改,但已经足够稳定。

除了四大浏览器的一致支持,Wasm也获得了主流编程语言的强力支持。C/C++是最先可以编译为Wasm的语言,因为Emscripten已经支持asm.js,只要把asm.js编译成Wasm即可。由于两项技术比较相似,这个编译工作并不难。2016年12月,Rust 1.14发布,开始实验性支持Wasm。2018年8月,Go 1.11发布,开始实验性支持Wasm。2019年3月,LLVM 8.0.0发布,正式支持Wasm。同年10月,Emscripten改为默认使用LLVM提供的Wasm编译后端直接生成Wasm。还有其他很多语言也都正式或通过第三方开源项目支持Wasm,这里就不一一列举了。

虽然诞生于Web和浏览器,但是Wasm并没有和Web或者浏览器绑定。相反,Wasm核心规范很少提及Web和浏览器,这就给Wasm未来的应用前景提供了更加广阔的想象空间。在浏览器之外,可能首先被Wasm吸引的就是区块链项目。目前已经有一些区块链基于Wasm技术实现智能合约平台,比如EOS。著名的以太坊项目也正在将其虚拟机(Ethereum Virtual Machine,EVM)切换到Wasm,并计划在以太坊2.0正式启用。开源世界对于Wasm技术的热情非常高,目前已经有许多高质量的开源Wasm实现,包括用C/C++、Rust、Go语言实现的Wasm解释器、AOT/JIT编译器。Wasm技术可谓前途一片光明。

以上简单介绍了Wasm技术的历史,下面简要介绍Wasm技术本身。

[1] 参考链接:http://asmjs.org/。