一文入门:WebAssembly

5,332 阅读4分钟

WebAssembly 介绍

出现 WebAssembly 的契机

JS 语言存在的缺陷

  1. JS只包含64位浮点数(double双精度浮点数),遵循IEEE754标准,这会导致运算不准确的问题(如:0.1 + 0.2),当然现今的解决方案也很多,如:mathjsTC39 BigInt、将结果通过toPrecision + parseFloat处理等;
  2. JS的弱类型可能导致混乱与Bug,现今可以通过TS解决;
  3. JS不能像编译语言一样高效;
  4. 在之前,若要进行web开发必须使用JS,虽然业界有各式各样的Transpiler,但归根结底还是将其它源码编译成了JS,受限于JS的特性

谷歌与火狐对支持编译语言的不同实现

谷歌Native Client(NaCl)(已废弃)

NaCl使用SFI技术,使浏览器可以安全地运行原生代码,并可以使用CPU的全部功能,PNaClNaCl增加了可移植性

PNaClNaCl的编译与运行

PNaCl通过Pepper与浏览器JS之间通信

NaCl的废弃

除Chrome以外,其它浏览器厂商认为使用NaCl(PNaCl),应用程序将运行在一个黑盒中,对其安全性产生质疑。最终2017年5月,NaCl被废弃,取而代之的是WebAssembly

火狐asm.js

asm.js为开发人员提供了一种将c/c++源码转换为JS的方法(JS的严格子集)。例如使用Emscripten,c/c++代码可以编译为asm.js,js引擎识别到asm.js将会将其直接转换为汇编,利用WebGL通过GPU执行asm.js,极大的提升运行速度。其次asm.js模块也可以与JS模块交互

JS的AOT编译

asm.js是WebAssembly最小可行产品(MVP)的基础

asm.js的缺点:asm.js是一个文本文件,在进行任何编译前必须通过网络传输,而Webassembly则采用二进制格式,其体积更小,因此传输效率更高

WebAssembly诞生

2015年4月,W3C成立WebAssembly工作组,用于监督与规范WebAssembly提案,倡导浏览器厂商使用一致性的规范。

WebAssembly的目标

  1. 规范浏览器厂商协作;
  2. JS使用WebAssembly代码像导入一个模块一样简单;
  3. 不取代JS引擎,仅仅是为其添加一个新特性。

WebAssembly实质

简介

WebAssembly是一个底层虚拟机,.wasm 是基于堆栈虚拟机的二进制指令格式,WebAssembly的设计目标是编译高级语言(c/c++/Rust等),提供可移植的结果,支持部署到web端及服务端

核心

  1. 核心规范
  2. JS API & Web API
  3. 二进制及文本格式(将文本格式代码序列化为AST最终编译为wasm二进制文件)

堆栈虚拟机

堆栈虚拟机由两个元素组成:栈(数据结构)、指令,栈存在两个操作:push、pop,遵循后进先出,栈还包括指向栈顶项的指针。指令表示要对栈里面项执行的操作。

示例,下面ADD指令会弹出栈顶两项,经过求和指令后,将结果推回栈:

WebAssembly编译

WebAssembly的MVP专注于C/C++,所以编译主要是通过Clang/LLVM/Emscripten等实现

WebAssembly语义阶段

当浏览器获取到.wasm文件(二进制),JS引擎将使用解码栈解码,将wasm文件转换为AST,执行类型检查,并将其解释为执行函数。示例图:

说明

阶段说明
Decoding解码,将二进制格式转换为模块
Validation验证,对解码后的模块进行验证(如类型检查),以确保模块良好且安全
Phase-1: Instantiation实例化第一阶段,通过Globals、Memories、Tables,生成初始化模块实例,并调用start()函数
Phase-2: Invocation实例化第二阶段,导出函数:从模块实例调用

应用场景

  • 图像/视频编辑
  • 游戏
  • 音乐流媒体、缓存
  • 图像识别
  • 直播视频
  • VR & AR

虽然以上应用也可以使用JS,但使用WebAssembly可以极大的提升其性能,并且通过二进制文件可以极大的减少js编译后文件(例如React的bundle)大小,在页面上加载实例化wasm模块可以加快代码执行(WebAssembly自身线程)

限制

  • 暂时不支持GC,C/C++的内存管理构建在语言中,而JAVA使用GC(程序不再使用的对象占用内存将被自动回收);
  • 不能直接访问DOM,可以通过JS、Emscripten完成操作
  • 对老版本浏览器不兼容,当前支持Webassembly的浏览器

Emscripten

Emscripten是一个LLVM-to-JS的编译器,其接受诸如Clang编译产出的LLVM位码输出,并将其转换为JS。Emscripten是一种构建、编译、运行asm.js的技术组合。

生成wasm模块,可以使用Emscripten SDK管理器:

其它

JS AST 示例

Webassembly 提案