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. Python能做什么,自学Python效果怎么样?

    短时间掌握一门技能是现代社会的需求.生活节奏越来越快,现在不是大鱼吃小鱼,而是快鱼吃慢鱼的时代,人的时间比机器的时间更值钱.Python作为一种轻量级编程语言,语言简洁开发快,没那么多技巧,受到众多追 ...

  2. SpringCloud番外篇-服务治理之Nacos

    一.Nacos概述 Nacos是阿里巴巴开源的服务注册中心,官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.html 从个人使用体验上看,nacos要比e ...

  3. 从比特币、以太坊、libra的不同特点认识move语言

    关于比特币.以太坊.libra,我们知道他们是不同的区块链应用,那么他们的根本差别在哪里呢. 其实,单从白皮书的标题,就可以大概看出三个项目在设计目标上的差异. 比特币的目标是 -- 可编程货币(Pr ...

  4. [考试反思]0807NOIP模拟测试14:承认

    一大排并列Rank#9之一. 考试题还没改完(而且并不会模拟退火)所以题解又只能咕了 然而并不想吐槽T2对sjzyz是原题导致4个AC里面有3个他们的 虽说这次的成绩不怎么样,但是这次的考试过程是全新 ...

  5. NOIP模拟 15

    因为OJ停机,正好写(tui)个总结(boke) 题解不想写了. 前两题题意没看懂,其实比较简单. 最后一题神仙,想放弃. (迪神貌似又在疯狂骂自己) (我这么辣鸡我...) (下面开始跑题) 这两天 ...

  6. Scrapy爬取豆瓣图书数据并写入MySQL

    项目地址 BookSpider 介绍 本篇涉及的内容主要是获取分类下的所有图书数据,并写入MySQL 准备 Python3.6.Scrapy.Twisted.MySQLdb等 演示 代码 一.创建项目 ...

  7. unittest使用总结

    unittest简介 Unittest是python内置的一个单元测试框架,主要用于自动化测试用例的开发与执行 简单的使用如下 import unittest class TestStringMeth ...

  8. javascript iframe跳转问题

    javascript iframe跳转问题如果在iframe里面有要点击跳转最外层的连接 要只能用<pre> <div onclick="parent.location.h ...

  9. C# 获取系统当前登录用户(管理员身份运行同样有效)

    今天学习下怎么用.Net获取系统当前登陆用户名,因为目前网上基本只有最简单的方式,但以管理员身份运行的话就会获取不到,所以特整理一下作为分享,最后附带参考文档,方便深究的童鞋继续学习. ======= ...

  10. T-SQL Part VIII: CROSS APPLY, OUTER APPLY

    除了CROSS JOIN, INNER JOIN, OUTER JOIN之外,T-SQL还提供了CROSS APPLY和OUTER APPLY这两个较为另类的Set操作符. 首先来看CROSS APP ...