webassembly介绍
本文介绍Webassembly的基本应用和接入方法,初步认识Webassembly技术
一、WebAssembly介绍
1、什么是WebAssembly
WebAssembly/wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新二进制格式,其目标就是充分发挥硬件能力以达到原生执行效率。是一种可以使用非Javascript的编程语言编写代码并且能在浏览器上运行的技术方案,实际上,是一种新的字节码格式。
2019年12月5日,WebAssembly正式成为World Wide Web Consortium (W3C)的标准,加入到HTML、CSS、JavaScript的行列,成为web的第四种语言。
可以打开浏览器控制台输入WebAssembly查看。
浏览器兼容性:https://caniuse.com/?search=WebAssembly
2、WebAssembly的应用
https://www.wasm.com.cn/docs/use-cases/
前端应用场景:
2、google地图:https://earth.google.com/static/wasm/
3、坦克大战:https://unity3d.com/learn/tutorials/projects/tanks-tutorial
4、微信小程序:https://developers.weixin.qq.com/miniprogram/dev/framework/performance/wasm.html
非*前端应用场景:
1、Fastly 与其他服务商的边缘云(参见[译][图文]标准化中的 WASI:在 web 之外运行 WebAssembly 的系统接口)
2、Node 与 npm(参见译文)插件系统。无论浏览器、IDE 还是任何其他地方的插件系统如果采用 WebAssembly 既能保证安全性避免插件代码越权,也能与开发语言解耦、有利于插件生态的繁荣。
3、Docker 的联合创始人Solomon Hykes 甚至说,如果在 2008 年有 WebAssembly + WASI(WebAssembly System Interface, WASM系统接口),就无需创建 Docker 了。
其他应用:https://blog.csdn.net/vhwfr2u02q/article/details/79235198
3、WebAssembly的优势
- 文件加载:WebAssembly文件体积更小,所以下载速度更快
- 解析:解码WebAssembly比解析JavaScript要快
- 编译和优化:编译和优化所需的时间较少,因为在将文件推送到服务器之前已经进行了更多优化,JavaScript需要为动态类型多次编译代码
- 重新优化:WebAssembly代码不需要重新优化,因为编译器有足够的信息可以在第一次运行时获得正确的代码
- 执行:执行可以更快,WebAssembly指令更接近机器码
- 垃圾回收:目前WebAssembly不直接支持垃圾回攻,垃圾回收都是手动控制的,所以比自动垃圾回收效率更高
- 安全:可以放hash和签名等等
二、WebAssembly工作原理
1、官方解读
WebAssembly是除了JavaScript以外,另一种可以在网页中运行的编程语言。过去如果你想在浏览器中运行代码来对网页中各种元素进行控制,只有JavaScript这一种选择。
WebAssembly与其他的汇编语言不一样,它不依赖于具体的物理机器。可以抽象地理解成它是概念机器的机器语言。
它可以从各类现有的其他高级语言写的业务库编译而来,还有Haskell、Go、C#、Rust、swift、obj-C的语言的一些WebAssembly编译工具或者已经编译成的WebAssembly代码库。
既然是经过编译而得来,可以将WebAssembly理解为是该库的低级语言代码版本,是一种类汇编语言。
可以把它理解成一个ES6语法写的js模块,既可以有导入又有导出,也可以没有导入只有导出。
2、两类文件
WebAssembly文件格式与源码阅读->.wasm文件和.wast文件
WebAssembly代码存储在.wasm文件内,这类文件是要浏览器直接执行的。 因为.wasm文件内是二进制文件,难以阅读,为了方便开发者查看,官方给出了对.wasm文件的阅读方法, 通过把.wasm文件通过工具转为.wast的文本格式,开发者可以在一定程度上理解这个.wast文件。 .wast文件是通过S-表达式(一种类似lisp语言(函数式程序设计)的代码书写风格)来写成的。 .wast文件和.wasm文件的关系,他们之间的相互转化,可以通过工具wabt(https://github.com/WebAssembly/wabt)
3、工作流程

某高级语言写的某功能库–>emscripten编译–>.wasm文件–>结合WebAssembly JS API–>浏览器中运行 完成一部分 用js写,而后依靠浏览器解释执行,会比较消耗性能 的工作,比如视频解码,OpenGL,OpenCV等。 简单来说,加载运行wasm代码的过程如下图所示。

详细的过程以及每个过程调用的API如下图。

三、运行WebAssembly
1、WebAssembly工具
中文官方介绍:https://www.wasm.com.cn/getting-started/developers-guide/
Emscripten工具:https://emscripten.org/docs/getting_started/downloads.html
在线转换工具:https://mbebenita.github.io/WasmExplorer/
2、WebAssembly API

WebAssembly.Mudule和WebAssembly.compile()
都是用来把一个wasm的arraybuffer对象编译成一个模块,前者是同步的,后者是异步的,后者使用更多。 前者使用方式:new WebAssembly.Mudule(buffer);后者使用方式:WebAssembly.compile(buffer); WebAssembly.Mudule本身也是抽象意义上的模块对象。这两种方式调用以后,返回值都是一个模块对象,该对象 有导入对象、导出对象和自定义片段(custom section)。
WebAssembly.Instance和WebAssembly.instantiate()
都是用来做实例化,前者是同步的,后者是异步的,后者使用更多。 前者使用方式:new WebAssembly.Instance();后者使用方式:WebAssembly.instantiate(); 前者有两个重载,一个是传入buffer和imports对象,这种调用一次性完成了编译和实例化两个步骤, 第二个重载是传模块对象和imports对象,这种调用只完成实例化步骤。 因此,实际上WebAssembly.instantiate()和WebAssembly.Instance的第二张重载调用功能上更接近。
3、示例
1、首先新建一个 C 语言文件,假设叫 math.c ,在里边实现 add 和 square 方法:
int add (int x, int y) {
return x + y;
}
int square (int x) {
return x * x;
}
|
2、生成wasm文件
emcc math.c -Os -s WASM=1 -s SIDE_MODULE=1 -o math.wasm |
在线编译:https://mbebenita.github.io/WasmExplorer/
3、前端工程加载wasm文件
/**
* @param {String} path wasm 文件路径
* @param {Object} imports 传递到 wasm 代码中的变量
*/
function loadWebAssembly(path, imports: any = {}) {
return fetch(path)
.then((response) => response.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: any) => {
console.log('instance', instance);
const { add, square } = instance.exports;
console.log('2 + 4 =', add(2, 4));
console.log('3^2 =', square(3));
console.log('(2 + 5)^2 =', square(add(2 + 5)));
});
|
4、window执行C/C++与js执行对比
js 斐波那契算法:
/**
* 斐波那契函数
* 0 1 1 2 3 5 8 ...
* @param x
*/
function fib (x) {
if(x <= 0) return 0;
if(x <= 2) return 1;
return fib(x - 1) + fib(x - 2);
}
console.time('测试 fib 执行速度');
fib(40);
console.timeEnd('测试 fib 执行速度');
// 测试 fib 执行速度: 751.1669921875 ms
|
c++斐波那契算法:
#include <iostream>
#include <ctime>
using namespace std;
int fib(int x)
{
if(x <= 0) return 0;
if(x <= 2) return 1;
return fib(x - 1) + fib(x - 2);
}
int main ()
{
int t1, t2;
t1 = clock();
fib(40);
t2 = clock();
cout << t2 - t1 << "ms" << endl;
return 0;
}
|
安装C++编译器:https://blog.csdn.net/cbb944131226/article/details/82940273
执行
g++ fib.cpp -o c // g++ fib.cpp -o c -O4 c.exe // 396ms |
编译fib函数 得到fib.wasm,浏览器加载加载fib.wasm
loadWebAssembly('fib.wasm').then((instance: any) => {
console.log('fib.instance', instance);
const { fib } = instance.exports;
console.time('测试 C++ fib 执行速度');
fib(40);
console.timeEnd('测试 C++ fib 执行速度');
});
// 测试 C++ fib 执行速度: 564.48681640625 ms
|
相关资源推荐:
(1)英文官网 http://webassembly.org/
(2)中文官网 http://webassembly.org.cn/
(3)MDN网址 https://developer.mozilla.org/zh-CN/docs/WebAssembly
(4)资料齐全 https://github.com/mbasso/awesome-wasm
(5)一篇文章 https://segmentfault.com/a/1190000008402872
(6)一篇文章 https://segmentfault.com/a/1190000008686643
(7)有编译工具链简单介绍 http://geek.csdn.net/news/detail/185592

浙公网安备 33010602011771号