WebAssembly 介绍
出现 WebAssembly 的契机
JS 语言存在的缺陷
- JS只包含64位浮点数(double双精度浮点数),遵循IEEE754标准,这会导致运算不准确的问题(如:0.1 + 0.2),当然现今的解决方案也很多,如:mathjs、TC39 BigInt、将结果通过toPrecision + parseFloat处理等;
- JS的弱类型可能导致混乱与Bug,现今可以通过TS解决;
- JS不能像编译语言一样高效;
- 在之前,若要进行web开发必须使用JS,虽然业界有各式各样的Transpiler,但归根结底还是将其它源码编译成了JS,受限于JS的特性
谷歌与火狐对支持编译语言的不同实现
谷歌Native Client(NaCl)(已废弃)
NaCl使用SFI技术,使浏览器可以安全地运行原生代码,并可以使用CPU的全部功能,PNaCl为NaCl增加了可移植性
PNaCl与NaCl的编译与运行
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的目标
- 规范浏览器厂商协作;
- JS使用WebAssembly代码像导入一个模块一样简单;
- 不取代JS引擎,仅仅是为其添加一个新特性。
WebAssembly实质
简介
WebAssembly是一个底层虚拟机,.wasm 是基于堆栈虚拟机的二进制指令格式,WebAssembly的设计目标是编译高级语言(c/c++/Rust等),提供可移植的结果,支持部署到web端及服务端
核心
堆栈虚拟机
堆栈虚拟机由两个元素组成:栈(数据结构)、指令,栈存在两个操作: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管理器: