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