NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

WebAssembly 完全入门:了解 wasm 的前世今身

  • 2019-10-22
  • 本文字数:4957 字

    阅读完需:约 16 分钟

WebAssembly完全入门:了解wasm的前世今身

接触 WebAssembly 之后,在 google 上看了很多资料。然而,这些资料对 WebAssembly 的使用、介绍、意义等方面的解释都比较模糊和笼统,总是令人感觉没有获得预期的收获,要么是因为文章中的例子自己去实操不能成功,要么就是不知所云、一脸蒙蔽。本着业务催生技术的态度,这篇文章就此诞生了。本文主要是对 WebAssembly 的背景做一些介绍,包括 WebAssembly 是怎么出现的?优势在哪儿?等等。

WebAssembly 是什么?

定义

首先我们给它下个定义。


WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。

例子

当然,我知道,即使你看了定义也不知道 WebAssembly 到底是什么东西。废话不多说,我们通过一个简单的例子来看看 WebAssembly 到底是什么。



上图的左侧是用 C++ 实现的求递归的函数。中间是十六进制的 Binary Code。右侧是指令文本。可能有人就问,这跟 WebAssembly 有个屁的关系?其实,中间的十六进制的 Binary Code 就是 WebAssembly。

编译目标

大家可以看到,其可写性和可读性差到无法想象。那是因为 WebAssembly 不是用来给各位用手 一行一行撸 的代码,WebAssembly 是一个 编译目标。什么是编译目标?当我们写 TypeScript 的时候,Webpack 最后打包生成的 JavaScript 文件就是编译目标。可能大家已经猜到了,上图的 Binary 就是左侧的 C++ 代码经过编译器编译之后的结果。

WebAssembly 的由来

性能瓶颈

在业务需求越来越复杂的现在,前端的开发逻辑越来越复杂,相应的代码量随之变的越来越多。相应的,整个项目的起步的时间越来越长。在性能不好的电脑上,启动一个前端的项目甚至要花上十多秒。这些其实还好,说明前端越来越受到重视,越来越多的人开始进行前端的开发。


但是除了逻辑复杂、代码量大,还有另一个原因是 JavaScript 这门语言本身的缺陷,JavaScript 没有静态变量类型。这门解释型编程语言的作者 Brendan Eich,仓促的创造了这门如果被广泛使用的语言,以至于 JavaScript 的发展史甚至在某种层面上变成了填坑史。为什么说没有静态类型会降低效率。这会涉及到一些 JavaScript 引擎的一些知识。

静态变量类型所带来的问题


这是 Microsoft Edge 浏览器的 JavaScript 引擎 ChakraCore 的结构。我们来看一看我们的 JavaScript 代码在引擎中会经历什么。- JavaScript 文件会被下载下来。


  • 然后进入 Parser,Parser 会把代码转化成 AST(抽象语法树)。

  • 然后根据抽象语法树,Bytecode Compiler 字节码编译器会生成引擎能够直接阅读、执行的字节码。

  • 字节码进入翻译器,将字节码一行一行的翻译成效率十分高的 Machine Code。


在项目运行的过程中,引擎会对执行次数较多的 function 记性优化,引擎将其代码编译成 Machine Code 后打包送到顶部的 Just-In-Time(JIT) Compiler,下次再执行这个 function,就会直接执行编译好的 Machine Code。但是由于 JavaScript 的动态变量,上一秒可能是 Array,下一秒就变成了 Object。那么上一次引擎所做的优化,就失去了作用,此时又要再一次进行优化。

asm.js 出现

所以为了解决这个问题,WebAssembly 的前身,asm.js 诞生了。asm.js 是一个 Javascript 的严格子集,合理合法的 asm.js 代码一定是合理合法的 JavaScript 代码,但是反之就不成立。同 WebAssembly 一样,asm.js 不是用来给各位用手 一行一行撸 的代码,asm.js 是一个 编译目标。它的可读性、可读性虽然比 WebAssembly 好,但是对于开发者来说,仍然是无法接受的。


asm.js 强制静态类型,举个例子。


function asmJs() {    'use asm';
let myInt = 0 | 0; let myDouble = +1.1;}
复制代码


为什么 asm.js 会有静态类型呢?因为像0 | 0这样的,代表这是一个 Int 的数据,而+1.1则代表这是一个 Double 的数据。

asm.js 不能解决所有的问题

可能有人有疑问,这问题不是解决了吗?那为什么会有 WebAssembly?WebAssembly 又解决了什么问题?大家可以再看一下上面的 ChakraCore 的引擎结构。无论 asm.js 对静态类型的问题做的再好,它始终逃不过要经过 Parser,要经过 ByteCode Compiler,而这两步是 JavaScript 代码在引擎执行过程当中消耗时间最多的两步。而 WebAssembly 不用经过这两步。这就是 WebAssembly 比 asm.js 更快的原因。

WebAssembly 横空出世

所以在 2015 年,我们迎来了 WebAssembly。WebAssembly 是经过编译器编译之后的代码,体积小、起步快。在语法上完全脱离 JavaScript,同时具有沙盒化的执行环境。WebAssembly 同样的强制静态类型,是 C/C++/Rust 的编译目标。

WebAssembly 的优势

WebAssembly 和 asm.js 性能对比

下面的图是 Unity WebGL 使用和不使用 WebAssembly 的起步时间对比的一个 BenchMark,给大家当作一个参考。可以看到,在 FireFox 中,WebAssembly 和 asm.js 的性能差异达到了 2 倍,在 Chrome 中达到了 3 倍,在 Edge 中甚至达到了 6 倍。通过这些对比也可以从侧面看出,目前所有的主流浏览器都已经支持 WebAssembly V1(Node >= 8.0.0)。


与 JavaScript 做对比

我自己在一个用create-react-app新建的项目中,分别对比了 WebAssembly 版本和原生 JavaScript 版本的递归无优化的 Fibonacci 函数,下图是这两个函数在值是 45、48、50 的时候的性能对比。



看图说话,这就是 WebAssembly 与 JavaScript 很实际的一个性能对比。几乎稳定的是 JavaScript 的两倍。

WebAssembly 在大型项目中的应用

在这里能够举的例子还是很多,比如 AutoCAD、GoogleEarth、Unity、Unreal、PSPDKit、WebPack 等等。拿其中几个来简单说一下。

AutoCAD

这是一个用于画图的软件,在很长的一段时间是没有 Web 的版本的,原因有两个,其一,是 Web 的性能的确不能满足他们的需求。其二,在 WebAssembly 没有面世之前,AutoCAD 是用 C++ 实现的,要将其搬到 Web 上,就意味着要重写他们所有的代码,这代价十分的巨大。


而在 WebAssembly 面世之后,AutoCAD 得以利用编译器,将其沉淀了 30 多年的代码直接编译成 WebAssembly,同时性能基于之前的普通 Web 应用得到了很大的提升。正是这些原因,得以让 AutoCAD 将其应用从 Desktop 搬到 Web 中。

Google Earth

Google Earth 也就是谷歌地球,因为需要展示很多 3D 的图像,对性能要求十分高,所以采取了一些 Native 的技术。最初的时候就连 Google Chrome 浏览器都不支持 Web 的版本,需要单独下载 Google Earth 的 Destop 应用。而在 WebAssembly 之后呢,谷歌地球推出了 Web 的版本。而据说下一个可以运行谷歌地球的浏览器是 FireFox。

Unity 和 Unreal 游戏引擎

这里给两个油管的链接自己体验一下。


WebAssembly 要取代 JavaScript?

答案是否定的,请看下图。



大家可以看到这是一个协作关系。WebAssembly 是被设计成 JavaScript 的一个完善、补充,而不是一个替代品。WebAssembly 将很多编程语言带到了 Web 中。但是 JavaScript 因其不可思议的能力,仍然将保留现有的地位。

什么时候使用 WebAssembly?

说了这么多,我到底什么时候该使用它呢?总结下来,大部分情况分两个点。


  • 对性能有很高要求的 App/Module/ 游戏。

  • 在 Web 中使用 C/C++/Rust/Go 的库举个简单的例子。如果你要实现的 Web 版本的 Ins 或者 Facebook, 你想要提高效率。那么就可以把其中对图片进行压缩、解压缩、处理的工具,用 C++ 实现,然后再编译回 WebAssembly。

WebAssembly 的几个开发工具

WebAssembly 的意义

在我的个人理解上,WebAssembly 并没有要替代 JavaScript,一统天下的意思。我总结下来就两个点。


  • 给了 Web 更好的性能。

  • 给了 Web 更多的可能关于 WebAssembly 的性能问题,之前也花了很大的篇幅讲过了。而更多的可能,随着 WebAssembly 的技术越来越成熟,势必会有更多的应用,从 Desktop 被搬到 Web 上,这会使本来已经十分强大的 Web 更加丰富和强大。

WebAssembly 实操

要进行这个实际操作,你需要安装上文提到过的编译器 Emscripten,然后按照 这个步骤去安装。以下的步骤都默认为你已经安装了 Emscripten。


安装步骤: http://webassembly.org.cn/getting-started/developers-guide/

WebAssembly 在 Node 中的应用

导入 Emscripten 环境变量

进入到你的 emscripten 安装目录,执行以下代码。


source emsdk/emsdk_env.sh
复制代码

新建 C 文件

用 C 实现一个求和文件test.c,如下。


int add(int a, int b) {    return a + b;}
复制代码

使用 Emscripten 编译 C 文件

在同样的目录下执行如下代码。


emcc test.c -Os -s WASM=1 -s SIDE_MODULE=1 -o test.wasm
复制代码


emcc就是 Emscripten 编译器,test.c是我们的输入文件,-Os表示这次编译需要优化,-s WASM=1表示输出 wasm 的文件,因为默认的是输出 asm.js,-s SIDE_MODULE=1表示就只要这一个模块,不要给我其他乱七八糟的代码,-o test.wasm是我们的输出文件。


编译成功之后,当前目录下就会生成test.wasm

编写在 Node 中调用的代码

新建一个 js 文件test.js。代码如下。


const fs = require('fs');let src = new Uint8Array(fs.readFileSync('./test.wasm'));const env = {    memoryBase: 0,    tableBase: 0,    memory: new WebAssembly.Memory({        initial: 256    }),    table: new WebAssembly.Table({        initial: 2,        element: 'anyfunc'    }),    abort: () => {throw 'abort';}}WebAssembly.instantiate(src, {env: env}).then(result => {    console.log(result.instance.exports._add(20, 89));}).catch(e => console.log(e));
复制代码

执行 test.js

运行以下代码。


node test.js
复制代码


然后就可以看到输出的结果 109 了。

WebAssembly 在 React 当中的应用

通过 fetch 的方法调用 直接用 fetch 的方式。大概的调用方式如下。


const fibonacciUrl = './fibonacci.wasm';const {_fibonacci} = await this.getExportFunction(fibonacciUrl);
复制代码


getExportFunction具体代码如下。


getExportFunction = async (url) => {    const env = {      memoryBase: 0,      tableBase: 0,      memory: new WebAssembly.Memory({        initial: 256      }),      table: new WebAssembly.Table({        initial: 2,        element: 'anyfunc'      })    };    const instance = await fetch(url).then((response) => {      return response.arrayBuffer();    }).then((bytes) => {      return WebAssembly.instantiate(bytes, {env: env})    }).then((instance) => {      return instance.instance.exports;    });    return instance;};
复制代码

通过 import C 文件来调用

先通过 Import 的方式来引进依赖。


import wasmC from './add.c';
复制代码


然后进行调用。具体的方式如下。


wasmC({  'global': {},  'env': {    'memoryBase': 0,    'tableBase': 0,    'memory': new WebAssembly.Memory({initial: 256}),    'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'})  }}).then(result => {  const exports = result.instance.exports;  const add = exports._add;  const fibonacci = exports._fibonacci;  console.log('C return value was', add(2, 5643));  console.log('Fibonacci', fibonacci(2));});
复制代码


详细的代码在这里: https://github.com/detectiveHLH/webassembly-in-react

写在后面

如今技术出现的越来越多,但是实际上在工作中能够用到的,越并不是那么多。其实很多大厂所输出的一些技术,都是有业务场景的,有业务做推动。而不是凭空造轮子。所以总结下来适合自己的才是最好的。当然不是说不要了解新技术,了解新技术跟上步伐是十分必要的。我们现在不用,不代表不需要了解。相反,以后再遇到类似的业务场景时,我们就会多一种选择,可以更加从容的对待。


本文转载自微信公众号:SH 的全栈笔记


2019-10-22 18:5514157

评论 2 条评论

发布
用户头像
请问kotlin/native什么时候支持wasm
2019-10-25 22:41
回复
现在支持了
2023-04-23 10:19 · 上海
回复
没有更多了
发现更多内容

接近8000字的Spring/Spring常用注解总结!安排!

Java你猿哥

spring Spring Boot ssm java

问:你是如何进行react状态管理方案选择的?

beifeng1996

前端 React

flutter系列之:创建一个内嵌的navigation

程序那些事

flutter 架构 大前端 程序那些事

一文吃透什么是低代码开发?

这我可不懂

低代码 低代码平台 JNPF

软件测试/测试开发 | 电商业务的性能测试(一): 必备基础知识

测试人

软件测试 自动化测试 测试开发

企业数字化转型的核心——工作流

力软低代码开发平台

算力网络智能选路新向导

鲸品堂

算力 电信运营商 算力网络 企业号 3 月 PK 榜

常见的ftp文件传输工具有哪些?推荐最佳ftp文件传输工具

镭速

什么大模型?我是时尚产业“大模王”!

白洞计划

AI AI制衣

精心整理SpringBoot学习笔记,从Web入门到系统架构

Java你猿哥

Java 面试 面经 校招 春招 java

4万字,阿里云《大型体育赛事云上实战精选》电子书发布!

阿里云视频云

云计算 视频云

问:React的useState和setState到底是同步还是异步呢?

beifeng1996

React

观测云产品更新|数据存储策略变更优化;新增支持创建重名的仪表板、笔记、自定义查看器;DQL 参数生效优先级调整等

观测云

数据存储 可观测 产品更新 观测云 可观测性用观测云

加速数据要素价值释放,用友打造高性能时序数据库

用友BIP

数据分析

​在行 | AI赋能,为行业发展创造全新可能

用友BIP

一场HttpClient调用未关闭流引发的问题

石臻臻的杂货铺

HttpClient

字节前端二面react面试题(边面边更)

beifeng1996

前端 React

IDO代币合约质押流动性挖矿分红系统开发(开发逻辑及源码)

系统开发咨询1357O98O718

80%的前端开发都答不上来的js异步面试题

loveX001

JavaScript 前端

美团面试全流程详解:一面 + 二面

Java你猿哥

ssm Java 面试 面经 java

为什么00后都不知道什么是报销?

用友BIP

差旅报销

好家伙!阿里最新版高并发系统设计涵盖了“三高”所有骚操作

Java你猿哥

java面试 面经 春招 java 八股文

两会共议北斗发展,华大北斗芯片领衔

江湖老铁

校招前端二面经典react面试题及答案

beifeng1996

前端 React

卓越工作的4个特征

凌晞

项目管理 技术管理 构架

JS模块化—CJS&AMD&CMD&ES6-前端面试知识点查漏补缺

loveX001

JavaScript 前端

腾讯前端一面常考面试题

loveX001

JavaScript 前端

2023我的前端面试小结

loveX001

JavaScript 前端

一文带你深度探析:软硬科技协同创新正当时

加入高科技仿生人

人工智能 科技 科技创新 ChatGPT

从智能合约到机器学习:NFT 与 AI 技术的结合

NFT Research

大数据 AI NFT

架构训练营模块七作业

gigifrog

架构训练营

WebAssembly完全入门:了解wasm的前世今身_大前端_SH的全栈笔记_InfoQ精选文章