WebAssembly是什么?
WebAssembly(wasm),是由 Google、Microsoft、Mozilla、Apple 等几家大公司合作发起的一个关于面向 Web 的通用二进制和文本格式的项目。
wasm是一个可移植、体积小、加载快并且兼容 Web 的全新格式。
wasm是一种新的字节码格式,旨在成为高级语言的编译目标,目前可以使用 C、C++、Rust、Go、Java、C# 等编译器来创建 wasm 模块。该模块以二进制的格式发送到浏览器,并在专有虚拟机上执行,与JavaScript虚拟机共享内存和线程等资源
为什么会有WebAssembly?
JavaScript的困境
- 语法太灵活导致大型项目开发困难
- 性能不能满足一些场景的需要
MicroSoft解决办法
MicroSoft 集结了 C# 的首席架构师以及 Delphi 和 Turbo Pascal 的创始人 Anders Hejlsberg 等明星整容,打造了 TypeScript。
TypeScript 它是 JavaScript 的一个严格超集,并添加了可选的静态类型和使用看起来像基于类的面向对象编程语法操作 Prototype。
Google解决方法
Google 在 2009 年在 V8 中引入了 JIT 技术(Just in Time Compiling),通过各种编译优化直接将 JavaScript 编译成运行在 CPU 上的机器码。JavaScript 的性能提升了 20 - 40 倍。
Google 的 Dart 为浏览器引入新的虚拟机去直接运行 Dart 程序以提升性能,然而主流浏览器支持有限,且用 Dart 开发的人不多
Mozilla解决方法
为了进一步 提高JIT 优化效率,继而提升 JavaScript 性能,浏览器鼻祖 Mozilla 推出了 asm.js
和 TypeScript 比较相似的是,asm.js 同样也是强类型的 JavaScript,但是他的语法则是 JavaScript 的子集,是为了 JIT 性能优化而专门打造的。
然而asm.js尚未完全标准化,浏览器支持不够好,语法太简单、有很大限制,开发效率低
WebAssembly的原理
和 JavaScript 解释运行不同的是,WebAssembly 的字节码和底层机器码很相似,可以快速的装载运行,因此性能相对于 JavaScript 的解释有大幅度的提升。也就是说,WebAssembly 并不是一门编程语言,而是一份字节码标准,需要使用高级编程语言编译出字节码放到 WebAssembly 的虚拟机中运行(有点像 Java )。浏览器厂商需要做的事情就是根据 WebAssembly 的规范实现虚拟机。
WebAssembly甚至比asm.js还快5%-7%
如何开发WebAssembly?
使用WebAssembly的过程可以归纳为三步:
- 使用强类型语言(如C++)编写代码
- 用工具(Emscripten)将代码转换成wasm文件
- 在页面中加载wasm模块,wasm模块可以直接执行或者作为一个库被调用
作为开发 WebAssembly的工具 。目前有binarian,这也是新诞生一种工具,通过与emscripten一同协作来产生WebAssembly code。此外,也可以用其他工具比如GCC 。
接下来,当你有了一个c++文件,就可以通过相应的编译命令,得到两个文件,.js和.WASM。这个js文件便可以直接在浏览器中运行。
同时,也可以在WASM中调用js,包括一些DOM APIs,通过使用EM_ASM这个命令:
使用高级语言编写wasm
//CALL JS FROM WASM
#include<emscripten.h>
int main(){
EM_ASM(
const elt = document.getElementById("hello-world");
elt.innerText = "Hello World";
);
return 0;
}
当然,不仅限于Web API,你同样也可以可以在C++中导入你在js中定义的函数。
JavaScript中调用wasm
<script>
/**
* @param {String} path wasm 文件路径
* @param {Object} imports 传递到 wasm 代码中的变量
*/
function loadWebAssembly (path, imports = {}) {
return fetch(path) // 加载文件
.then(response => response.arrayBuffer()) // 转成 ArrayBuffer
.then(buffer => WebAssembly.compile(buffer))
.then(module => {
imports.env = imports.env || {}
// 开辟内存空间
imports.env.memoryBase = imports.env.memoryBase || 0
if (!imports.env.memory) {
imports.env.memory = new WebAssembly.Memory({ initial: 256 })
}
// 创建变量映射表
imports.env.tableBase = imports.env.tableBase || 0
if (!imports.env.table) {
// 在 MVP 版本中 element 只能是 "anyfunc"
imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
// 创建 WebAssembly 实例
return new WebAssembly.Instance(module, imports)
})
}
//调用
loadWebAssembly('./math.wasm')
.then(instance => {
const add = instance.exports._add//取出c里面的方法
const square = instance.exports._square//取出c里面的方法
console.log('10 + 20 =', add(10, 20))
console.log('3*3 =', square(3))
console.log('(2 + 5)*2 =', square(add(2 + 5)))
})
</script>
WebAssembly应用场景
更好的让一些语言和工具可以编译到 Web 平台运行。
图片/视频编辑。
游戏:
需要快速打开的小游戏
AAA 级,资源量很大的游戏。
游戏门户(代理/原创游戏平台)
P2P 应用(游戏,实时合作编辑)
音乐播放器(流媒体,缓存)
图像识别
视频直播
VR 和虚拟现实
CAD 软件
科学可视化和仿真
互动教育软件和新闻文章。
模拟/仿真平台(ARC, DOSBox, QEMU, MAME, …)。
语言编译器/虚拟机。
POSIX用户空间环境,允许移植现有的POSIX应用程序。
开发者工具(编辑器,编译器,调试器…)
远程桌面。
VPN。
加密工具。
本地 Web 服务器。
使用 NPAPI 分发的插件,但受限于 Web 安全协议,可以使用 Web APIs。
企业软件功能性客户端(比如:数据库)
WebAssembly典型案例
1.TeaVM — 将 JVM 字节码翻译成 JavaScript 和 WebAssembly
2.Figma — 基于浏览器的多人实时协作 UI 设计工具
3.Google Earth — 支持各大浏览器的 3D 地图,而且运行流畅
-
Magnum — 跨平台的 OpenGL 图形引擎
-
Egret Engine — 最受欢迎的 HTML 5 游戏引擎,让游戏引擎快三倍
-
Blazor — 让 .NET 代码也能在浏览器运行
-
Web-DSP — 使用浏览器就能即时制作多媒体影音特效
-
Walt — 用 JavaScript 语法也能快速开发原生的极速应用
WebAssembly展望。
…将来 WebAssembly 将会支持不带内存垃圾回功能的的语言
…规范将会为在未来为 wasm 提供访问指定平台的接口,这样你就可以不用在你的程序中内置 JavaScript。
…未来引入Source map的支持,以更方便的调试程序
…Wasm 将会接近实现原生的线程(比如,C++ 风格的线程)
…WebAssembly 拥有和早年 Java 使用 Applets 来实现可移植性的同样的目标
WebAssembly 主要是为了解决 JS 的性能瓶颈,也就是说 WebAssembly 适合用于需要大量计算的场景,例如:
- 在浏览器中处理音视频, flv.js 用 WebAssembly 重写后性能会有很大提升;
- React 的 dom diff 中涉及到大量计算,用 WebAssembly 重写 React 核心模块能提升性能。
- Safari 浏览器使用的 JS 引擎 JavaScriptCore 也已经支持 WebAssembly,RN 应用性能也能提升;
VIDAA中使用WebAssembly的场景:
- Web SDK,替代UserJS,以获得更高的安全性及性能
- HiPlayer,音视频相关
- Model JS?
- UI Biz ?
- HiSense UI ?
附:名词解释
AOT or JIT
AOT: Ahead-of-Time compilation
必须是强类型语言,编译在执行之前,编译直接生成 CPU 能够执行的二进制文件,执行时 CPU 不需要做任何编译操作,直接执行,性能最佳。
JIT: Just-in-Time compilation
没有编译环节。执行时根据上下文生成二进制汇编代码,灌入 CPU 执行。JIT 执行时,可以根据代码编译进行优化,代码运行时,不需要每次都翻译成二进制汇编代码,V8 就是这样优化 JavaScript 性能的。
由于 JavaScript 的动态语言类型的特性已无法改变,所以只能采用 JIT 的形式对性能进行优化。
个人问题: