浅谈WebAssembly

政采云技术团队.png

北洛.png

1.引言

​ 相信大家都被“如何高效的熟记英语单词”这个问题困扰过,当每次下定决心这次一定要坚持背单词,一定要考过四六级,然后拿起单词书,开始:“abandon,abandon,放弃,放弃......”。果然,有些事情还没开始就已经结束了。

​ 后来有些人就发现英语单词构成有其规律,好多复杂单词是由若干个单词组成的,词的意义也是由组成单词的词根体现出来的。发现这一规律,背单词也就轻松很多,当然最重要的还是坚持。

​ 看完上面这两段话,可能会好奇,这和今天分享的技术有关系吗?结果明显是:当然没有,但这确是我们了解这项技术的开端。

​ WebAssembly,我们可以将其拆做两部分:Web —— 前端浏览器,Assembly —— 汇编。我们就可以大胆的猜一下:这是一门应用在前端浏览器上的汇编技术。那事实确实如此吗,以及它是怎么做到的,有什么作用,我们带着疑问往下走。

2.概念

​ 先上一段官方文档,专业一点。

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.
复制代码

​ 简单翻译过来就是:

WebAssembly(缩写为Wasm)是基于堆栈的虚拟机的二进制指令格式。Wasm被设计为编程语言的可移植编译目标,支持在web上部署客户端和服务器应用程序。
复制代码

​ 概括一下,它是一种将用一种编程语言编写的代码转换为浏览器可理解的机器代码的技术,能让程序以接近原生的效率运行在Web浏览器中。它有如下特点:

  • 高效

    WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率

  • 安全

    WebAssembly 运行在一个沙箱化的执行环境中,甚至可以在现有的 JavaScript 虚拟机中实现。在web环境中,WebAssembly将会严格遵守同源策略以及浏览器安全策略。

  • 开放

    WebAssembly 设计了一个非常规整的文本格式用来、调试、测试、实验、优化、学习、教学或者编写程序。可以以这种文本格式在web页面上查看wasm模块的源码

  • 标准

    WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的。WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非web环境下(重点,要考的)。

3.因何而来

3.1 js历史

​ 要说 wasm 因何而来,那就不得不谈一谈 javascript 了。 js 是一门动态类型的语言,编写程序时无需考虑变量类型,而且还可以运行时改变类型。对于我们开发者,确实很方便,但对于运行它的引擎就很有问题了。我们先来介绍一下 js 的编译运行过程:

image.png

上图是 js 编译运行的一个大致工作流程。我们来看一看 js 代码在引擎中会经历什么。

  • 首先加载我们所写的 js 代码。

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

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

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

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

3.2 asm.js

​ 后来就出现了一门技术 —— asm.js,可以说是wasm的前身。简单介绍一下:asm.js 是一个 js 的严格子集,合理合法的 asm.js 代码一定是合理合法的 js 代码,但是反之就不成立。同 wasm 一样,asm.js 也是一个编译目标,asm.js 的语法利用了一些标注让 js 的变量成为强类型的,所以它的出现就解决了上述 js 原来动态变量带来的问题。

​ asm.js 通常不直接编写,而是作为一种通过编译器生成的中间语言,该编译器获取 C++ 或其他语言的源代码,然后输出 asm.js。

例如下边的 C 语言代码。

int f(int i) {
  return i + 1;
}
复制代码

经过编译器编译会生成下边的 js 代码。

function f(i) {
  i = i|0;
  return (i + 1)|0;
}
复制代码

​ 所以在编译后它本身也是 js 代码,这就导致,在编译运行过程中它和 js 一样,需要经过 Parser,要经过 ByteCode Compiler,而这两步是 js 代码在引擎执行过程当中消耗时间最多的两步。

3.3 wasm

​ 而wasm解决了这个问题,目前 v8 引擎内置了 wasm 运行时,wasm就可以直接跳过复杂漫长的 Parse 以及 ByteCode Compiler 两步,直接可以运行,并且也解决了 js 动态变量的问题。

3.4 demo样例

该小节主要用 C 实现一个基本 demo,然后编译成 wasm,并通过 node.js(node 内置 v8),以及 web 方式运行,给大家一个直观的体验。

3.4.1 首先安装编译 wasm 的工具 —— Emscripten

mac 上安装:

$ git clone https://github.com/emscripten-core/emsdk.git
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest
$ #设置环境变量
$ source ./emsdk_env.sh --build=Release
复制代码

3.4.2.用 C 语言实现 hello-world

3.4.2.1 创建 hello.c 文件,并实现逻辑

cat << EOF > hello.c
> #include <stdio.h>
> int main(int argc, char ** argv) {
>   printf("Hello, world!\n");
> }
> EOF
复制代码

3.4.2.2 编译

emcc hello.c -o hello.html
复制代码

编译结果:

image.png

可以发现,工具已经帮我生成了 html、js 和 wasm 文件。

3.4.2.3 通过node环境运行

image.png

3.4.2.4 通过web展示

 #这里是起了一个emsdk提供的服务,方便测试
 emrun --no_browser --port 8080 .
复制代码

结果展示,我们可以发现在页面上输出了文本:

image.png

应用场景

​ 通过上述的例子,我们不难猜出 wasm 的用途,当一些功能只能通过后端服务实现,将处理结果传回前端进行展示时,我们不妨可以将后端服务代码编译成 wasm 运行在浏览器中,就可以一定程度的提高前端性能,降低了后端服务的压力。

​ 目前市场长已有众多 wasm 的应用场景。就比如: AutoCAD:

​ 这是一个用于画图的软件,在很长的一段时间是没有 Web 的版本的,原因有两个,其一,是 Web 的性能的确不能满足他们的需求。其二,在 WebAssembly 没有面世之前,AutoCAD 是用 C++ 实现的,要将其搬到 Web 上,就意味着要重写他们所有的代码,这代价十分的巨大。而在 WebAssembly 面世之后,AutoCAD 得以利用编译器,将其沉淀了 30 多年的代码直接编译成 WebAssembly,同时性能基于之前的普通 Web 应用得到了很大的提升。正是这些原因,得以让 AutoCAD 将其应用从 Desktop 搬到 Web 中。

扩展——始于 Web,不止在 Web

​ 刚才讲到,浏览器之所以能运行 wasm,是因为它的引擎中内嵌了 wasm 的 runtime,也就是说当我们把这个 runtime 拿出来放在任何一个平台上时,那就可以编译运行我们的 wasm 模块,也就是我们在概念中提到的标准。

​ 例如: Envoy,Envoy 是面向服务架构设计的L7代理和通信总线,核心是一个 L3/L4 网络代理。可插入 filter 链机制允许开发人员编写 filter 来执行不同的 TCP 代理任务并将其插入到主体服务中。Envoy 还支持额外的 HTTP L7 filter 层。可以将 HTTP filter 插入执行不同任务的 HTTP 连接管理子系统。如下图所示:

image.png

​ 目前 Envoy 提供了三种扩展 Envoy 功能的方式:

​ 1.编写 C++ 扩展

​ 这种方式直接在 Envoy 基础上编写 C++ 代码进行功能增强,相当于修改源码,实现自定义的 filter 之后,重新编译 Envoy 源码成新的二进制可执行文件,完成现有业务的升级替换。这样就会导致每次扩展功能都要重新编译并重启 Envoy,不利于功能的开发与迭代。

​ 2.编写 lua 脚本

​ 目前 Envoy 支持用 lua 脚本动态扩展其功能,这样就解决了方式1中的问题,但目前官方文档中描述:lua 脚本是实验性的,在生产环境中使用要自担风险, 不建议使用。

​ 3.通过 wasm 扩展

​ Envoy 内嵌了基于 LLVM 的 WAVM 与 V8 两个 C/C++ Wasm 运行时,这就说明 Envoy 内可以支持编译运行 wasm,我们可以通过其他语言编写对应扩展代码,编译成 wasm 后动态插入到 Envoy 的 Filter Chain 中实现动态功能扩展。

​ 通过 wasm 做功能扩展的不止 Envoy,还有 Dapr、SOFAMson 等等,围绕 wasm 本身也随之出现了若干产品:WebAssembly Hub,可以让 wasm 部署像 Dokcer 部署一样。目前情况来看,wasm 的应用场景很多,发展前景较好,社区也很活跃,大家感兴趣的话可以多多了解。

参考链接:

www.infoq.cn/article/lwl…

www.wasm.com.cn

推荐阅读

基于APT(注解处理器)实现 Lombok 的常用注解功能

浅谈 tcp 保活机制

CDH6.3.2 升级 Spark3.3.0 版本

从源码看 Lucene 的文档写入流程

ElasticSearch 文档分值 score 计算&聚合搜索案例分析

招贤纳士

政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有 500 多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 [email protected]

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

政采云技术团队.png

猜你喜欢

转载自juejin.im/post/7147845865456009224