纵论WebAssembly,JS在性能逆境下召唤强援
webassembly的作用
-
wasm: 一种体积小、加载快并且可以在Web浏览器端运行的底层二进制数据格式,并且可以由C++等语言转化而来
-
webassembly的操作接口:例如WebAssembly.instantiate就可以将一份wasm文件编译输出为JS能够直接调用的模块对象
webassembly项目的编码流程
-
性能无强关的部分用JS编写
-
性能强相关的,并且需要大量本地运算的部分,先用C++/Rust编写,通过命令行工具转化为wasm代码后让JS调用
玄学的webassembly性能提升
当您使用WebAssembly时,不要总是期望得到20倍的加速。您可能只得到2倍的加速或者20%的加速。或者,如果您在内存中加载非常大的文件时,或者需要在WebAssembly和JavaScript之间进行大量通信时,那么速度可能会变慢。 作者:Robert 《Level Up With WebAssembly》一书的作者,同时也是一位生物信息学软件工程师
在上面的文章的作者Robert,做了这样一个实验,他使用 seqtk,一个用C编写的评估DNA测序数据质量(通常用于操作这些数据文件)的软件,去对比webassembly相对于普通JS带来的性能提升
-
第一步:运行序列分析软件seqtk,对比性能:9倍提升
-
第二步:删除不必要的printf输出,对比性能:13倍提升
-
第三步:去除函数的重复调用后,对比性能:21倍提升
文章链接 作者:detectiveHLH
-
对于长文本(2M文本) 的密集计算,webassembly的性能提升很大
-
对于短文本("IVWEB")的密集计算,webassembly和纯JS性能相差无几
webassembly的兼容
实战 WebAssembly
-
编写Rust代码,然后通过wasm-pack转化成wasm代码
-
编写C/C++代码,然后通过Emscripten转化成wasm代码
备注:Rust是一门高性能的系统编程语言
通过Rust接入WebAssembly
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
2.安装编译工具wasm-pack(相当于前端的babel)
cargo install wasm-pack
cargo new --lib hello-wasm
初始化的文件夹如下所示
extern crate wasm_bindgen; use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn is_odd(n: u32) -> bool { n % 2 == 1 }
5.修改配置文件Cargo.toml
[package] name = "hello-wasm" version = "0.1.0" authors = ["作者名"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
-
dependencies中必须要有wasm-bindgen这个依赖
-
同时还要指定crate-type = ["cdylib"],否则转化不能成功
wasm-pack build --scope [自己的名字] // My Example wasm-pack build --scope penghuwan
编译开始
1.cd pkg
2.npm publish --access=public
const js = require("hello-wasm"); js.then(js => { const num1 = js.is_odd(3); const num2 = js.is_odd(4); console.log(num1); console.log(num2); });
9.浏览器输出
通过C/C++接入WebAssembly
备注:如果没有将source ./emsdk_env.sh写入到启动文件中的话,那么每次使用前都要在给定目录下运行一遍
#include <stdio.h> int main(int argc, char ** argv) { printf("Hello World"); }
3.用命令行编译它
emcc h.c -s WASM=1 -o h.js
WebAssembly相关的接口 API
-
exports属性: 一个对象,该对象包含从WebAssembly模块实例导出的所有函数属性
-
exports属性:一个数组,内容是所有已声明的接口的描述。
-
imports属性和:一个数组,内容是所有已声明的引用的描述。
-
参数:包含你想编译的wasm模块二进制代码的ArrayBuffer的类型实例
{
module: 一个被编译好的 WebAssembly.Module 对象.
instance: 一个WebAssembly.Instance对象
}
Example
fetch('simple.wasm').then(response => response.arrayBuffer() ).then(bytes => WebAssembly.instantiate(bytes) ).then(result => result.instance.exports // exports是wasm中输出的 );
webassembly的未来展望
-
多线程
-
SIMD(单指令流多数据流)
-
64位寻址
-
流式编译(在下载的同时编译 WebAssembly 文件)
-
分层编译器
-
隐式 HTTP 缓存
webassembly的使用场景及其限制
-
其实在大多数场景下我们都不需要用到webassembly。因为V8等JS引擎的优化带来了巨大的性能提升,已经足够让JS应对绝大多数的普通场景了,所以只有在以上的少数场景下,我们才需要做这种“二次提升”
-
和很多其他特性一样,兼容性同样是webassembly的一道坎,现代浏览器虽然支持度良好,但是在国内IE泛滥的特殊情况下, 这仍然是对webassembly的一个挑战。不过在桌面应用上或者一些对兼容性要求较低的工具型网页运用上,webassembly已经生根发芽,甚至能够遍地开花。
webassembly的产品案例