webassembly的作用

webassembly是一种底层的二进制数据格式和一套可以操作这种数据的JS接口的统称。我们可以认为webassembly的范畴里包含两部分
  • wasm: 一种体积小、加载快并且可以在Web浏览器端运行的底层二进制数据格式,并且可以由C++等语言转化而来

  • webassembly的操作接口:例如WebAssembly.instantiate就可以将一份wasm文件编译输出为JS能够直接调用的模块对象

打破性能瓶颈
一直以来,我们都比较关心JS的运行速度问题,V8引擎解决了绝大多数情况下遇到的问题,但是少数情况下我们进行大量本地运算的时候,仍然可能遇到性能瓶颈,需要优化,这个时候webassembly的作用就凸现出来了 

webassembly项目的编码流程

  • 性能无强关的部分用JS编写

  • 性能强相关的,并且需要大量本地运算的部分,先用C++/Rust编写,通过命令行工具转化为wasm代码后让JS调用

 

玄学的webassembly性能提升

webassembly相对于纯JS的性能提升是随具体场景和条件的变化而变化的 

当您使用WebAssembly时,不要总是期望得到20倍的加速。您可能只得到2倍的加速或者20%的加速。或者,如果您在内存中加载非常大的文件时,或者需要在WebAssembly和JavaScript之间进行大量通信时,那么速度可能会变慢。 作者:Robert 《Level Up With WebAssembly》一书的作者,同时也是一位生物信息学软件工程师

参考链接

在上面的文章的作者Robert,做了这样一个实验,他使用 seqtk,一个用C编写的评估DNA测序数据质量(通常用于操作这些数据文件)的软件,去对比webassembly相对于普通JS带来的性能提升

一.Robert的对比测试结果
下面是他的测试结果
  • 第一步:运行序列分析软件seqtk,对比性能:9倍提升

  • 第二步:删除不必要的printf输出,对比性能:13倍提升

  • 第三步:去除函数的重复调用后,对比性能:21倍提升

 
当然,上面的概括也许太过简略,大家可以看看Robert的原文以得到更为详细的认识
 
二.运行Fibonacci函数的性能对比
有位博主,对比了运行递归无优化的Fibonacci函数的时候,WebAssembly版本和原生JavaScript版本的性能差距,下图是这两个函数在值是45、48、50的时候的性能对比。

文章链接 作者:detectiveHLH

三.IVweb的的性能对比测试
IVWeb团队对长度不同的文本进行加密处理,对比webassembly相对于纯JS的性能提升,结果发现
  • 对于长文本(2M文本) 的密集计算,webassembly的性能提升很大

  • 对于短文本("IVWEB")的密集计算,webassembly和纯JS性能相差无几

第一组测试:2M长文本100000 次加密处理
 
第二组测试:"ivweb"短字符加密100000 次

资料来源

从上面的资料中我们了解到,webassembly性能提升的确存在,但是这个提升的范围是随条件和场景而变化的,需要遵循一定的原则

webassembly的兼容

下面是我在can i use上查到的结果,可以看到在现代浏览器上兼容良好,覆盖率达到88%。主要的问题在于IE浏览器不支持(IE11) 
IE兼容解决方案
Internet Explorer 11 是最后一个占有很大的市场份额,但不支持wasm的浏览器。我们可以通过 binaryen 项目的 wasm2js 工具,将我们的 WebAssembly 编译成 JavaScript,就可以获得 IE11 的大部分支持了
 

实战 WebAssembly

在浏览器中使用WebAssembly主要有两种方式:
  • 编写Rust代码,然后通过wasm-pack转化成wasm代码

  • 编写C/C++代码,然后通过Emscripten转化成wasm代码

备注:Rust是一门高性能的系统编程语言

通过Rust接入WebAssembly

《Rust 和 WebAssembly 用例》

1.安装rustup,初始化Rust环境,它会顺带安装cargo等工具(相当于前端的Node安装)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

2.安装编译工具wasm-pack(相当于前端的babel)

cargo install wasm-pack
3.创建一个文件夹,进入后运行下面代码,初始化一个Rust 项目
cargo new --lib hello-wasm

初始化的文件夹如下所示

4.修改lib.rs,改为以下几段Rust代码,这段代码的is_odd是一个判断数字是否为奇数的方法
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn is_odd(n: u32) -> bool {
n % 2 == 1
}

5.修改配置文件Cargo.toml

这个文件和我们的package.json有点像,我们就依样画葫芦,这个文件大概要写成下面这个样子 
[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"],否则转化不能成功

6.运行以下命令进行编译转化
wasm-pack build --scope [自己的名字]
// My Example
wasm-pack build --scope penghuwan

编译开始

编译成功后,新增了pkg文件夹和target文件夹
 
让我们看看pkg文件夹下的文件有哪
 
 
7. 将包发布到npm
1.cd pkg 
2.npm publish --access=public
8.安装刚刚发布的wasm模块,并通过webpack工具加载后,在浏览器运行以下代码
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

1.首先要按照文档下载编译工具emscripten

备注:如果没有将source ./emsdk_env.sh写入到启动文件中的话,那么每次使用前都要在给定目录下运行一遍

2.创建一个文件h.c,写入以下代码
#include <stdio.h>

int main(int argc, char ** argv) {
printf("Hello World");
}

3.用命令行编译它

emcc h.c -s WASM=1 -o h.js
生成文件如下图所示 
 
 
4.运行生成的h.js,则可看到输出了Hello World

WebAssembly相关的接口 API

看了上面的案例,你可能会觉得有些奇怪:怎么我们没有涉及浏览器提供的webassembly的API呀?
其实是有的,只不过在工具编译的时候自动帮忙填写了一些API而已,我们看下上面从h.c编译出来的h.js的一些片段就知道了 
下面我们就来介绍下怎么手动去写这些API
 
接口
>> WebAssembly.Instance
实例包含所有的 WebAssembly 导出函数 ,允许从JavaScript 调用 WebAssembly 代码.
 
对象属性
  • exports属性: 一个对象,该对象包含从WebAssembly模块实例导出的所有函数属性

>> WebAssembly.Module 
包含已经由浏览器编译的无状态 WebAssembly 代码,可以高效地与 Workers 共享、缓存在 IndexedDB 中,和多次实例化。 
 
对象属性
  • exports属性:一个数组,内容是所有已声明的接口的描述。

  • imports属性和:一个数组,内容是所有已声明的引用的描述。

参考链接

方法
>> WebAssembly.instantiate
它是编译和实例化 WebAssembly 代码的主要方法
  • 参数:包含你想编译的wasm模块二进制代码的ArrayBuffer的类型实例

返回值: 一个Promise, resolve后的值如下所示
{
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适用于JS难以解决的大计算量的应用场景,如图像/视频编辑、计算机视觉,3D游戏等等。在这些场景下,webassembly能够大限度地提高速度,弥补JS的缺陷和硬伤。
 
同时在另一方面,我们也需要认识到以下几点:
  1. 其实在大多数场景下我们都不需要用到webassembly。因为V8等JS引擎的优化带来了巨大的性能提升,已经足够让JS应对绝大多数的普通场景了,所以只有在以上的少数场景下,我们才需要做这种“二次提升”

  2. 和很多其他特性一样,兼容性同样是webassembly的一道坎,现代浏览器虽然支持度良好,但是在国内IE泛滥的特殊情况下, 这仍然是对webassembly的一个挑战。不过在桌面应用上或者一些对兼容性要求较低的工具型网页运用上,webassembly已经生根发芽,甚至能够遍地开花。

webassembly的产品案例

设计工具Figma
一般情况下,为了使用速度,设计工具都会选择Adobe等本地应用,而不会选择浏览器网页应用,而能够同时打开十几个画板也没有卡顿的Figma正在尝试改变这一认知,webassembly让它具有高效流畅的体验 
 
白鹭游戏引擎
白鹭游戏引擎是一套HTML5游戏开发解决方案,它衍生了开发莽荒纪同名手游、梦道、坦克风云的等游戏,而利用 WebAssembly,白鹭引擎让游戏运行性能提升了300%。
 
OpenGL 图形引擎Magnum
Magnum 是一款数据可视化 OpenGL 图形处理引擎,也采用了WebAssembly支撑浏览器环境的应用

参考资料

纵论WebAssembly,JS在性能逆境下召唤强援的更多相关文章

  1. 准备:新V8即将到来,Node.js的性能正在改变

    V8的Turbofan的性能特点将如何对我们优化的方式产生影响 审阅:来自V8团队的Franziska Hinkelmann和Benedikt Meurer. **更新:Node.js 8.3.0已经 ...

  2. Babylon.js官方性能优化文档中文翻译

    在这里列出Babylon.js官方性能优化文档的中英文对照,并在CardSimulate项目里对其中的一些优化方法进行实践. How To 如何 Optimize your scene 优化你的场景 ...

  3. 多个JS文件性能优化

    页面中引入的JS文件是阻塞式加载的,这样会影响页面性能.以下是JS文件性能优化方法: 一:将所有的<script>标签放到页面底部,也就是</body>闭合标签之前,这能确保在 ...

  4. js开发性能(一)

    随着js技术的发展,性能问题开始被越来越多的人关注,最近了解了一些关于前端性能的问题,这里主要讨论一下在js脚本加载和执行的过程中,我们应该怎么样来提高js的性能. js脚本的处理 初学前端的时候,我 ...

  5. [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)

    [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...

  6. js获取键盘按下的键值event.keyCode,event.charCode,event.which的兼容性

    js获取键盘按下的键值有event.keyCode,event.charCode和event.which 其中: 谷歌浏览器对event.keyCode,event.charCode和event.wh ...

  7. JS、jqueryie6浏览器下使用js无法提交表单的解决办法

    -----------------------JS.jqueryie6浏览器下使用js无法提交表单的解决办法---------------------------------------------- ...

  8. css配合js模拟的select下拉框

    css配合js模拟的select下拉框 <!doctype html> <html> <head> <meta charset="utf-8&quo ...

  9. JS年月日三级联动下拉框日期选择代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. Linux上安装mysql,实现主从复制

    MYSQL(mariadb) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可.开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL闭源的 ...

  2. 面向对象的7个设计原则->开车理解->贴近生活

    设计模式在我们的开发中是不可或缺的一部分,很多人会说,我没用那些设计模式啊,我也开发的挺好的,其实不然,我们在开发中都用到了这些设计模式,只不过我们并没有在意这些,今天我就用开车的方法来解释一下我们的 ...

  3. kali linux 开启配置ssh服务

    1.    一.配置SSH参数 修改sshd_config文件,命令为: vi /etc/ssh/sshd_config 将#PasswordAuthentication no的注释去掉,并且将NO修 ...

  4. 第三十三章 System V共享内存与信号量综合

    用信号量解决生产者.消费者问题 实现shmfifo ip.h #ifndef _IPC_H #define _IPC_H #include <unistd.h> #include < ...

  5. 第二十六章 system v消息队列(二)

    msgsnd int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 作用: 把一条消息添加到消息队列中 参数: msqi ...

  6. 说说 Python3 中的数字处理

    最近在处理订单相关的问题,踩了数字的一些坑,在此记录下. 其中有问题的代码涉及金额比较,便于描述,假设了下面一段代码 def is_paid(pay_price, paid_price): retur ...

  7. mysql中的锁机制之悲观锁和乐观锁

    1.悲观锁? 悲观锁顾名思义就是很悲观,悲观锁认为数据随时就有可能会被外界进行修改,所以悲观锁一上来就会把数据给加上锁.悲观锁一般都是依靠关系型数据库提供的锁机制,然而事实上关系型数据库中的行锁,表锁 ...

  8. js内容溢出用省略号(...)表示

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. java数组、字符串拼接

    1. 数组实现拼接 int[] arr ={11,22,33,44,55,66}; System.out.print("["); for (int i = 0; i <arr ...

  10. Win7无法远程桌面

    Win7在设置里开启允许其他计算机远程连接,但局域网计算机还是连不上: 然后点击上面的为远程桌面启用windows防火墙例外,发现远程桌面是允许的: 实际上这个不是,问题的真正原因在于用于远程的338 ...