WebAssembly生态及关键技术综述

WebAssembly 作为字节码和内存模型规范看起来非常简单且实现起来很有趣,随着WebAssembly 规范的演进,新技术不断涌现,在应用场景中构建自己的生态系统,并不断发展成为一个成熟的平台。

1716236b64590a36c99bdb51c398b3de.png

按上图所示开发模式,开发者可以通过特定的语言生态开发、发布和执行应用; 那么选择WebAssembly 的动机是什么呢?社区有很多这方面的讨论,总结可能有如下几方面。

  • 首先,大型软件需要解决不同开发语言的协作问题(交互和集成),不同语言的交互一般需要语言特定的 FFI ,而没有统一的集成方式和接口(组合爆炸💥问题);交互过程中会引入额外的损耗(内存和性能)

  • 其次,不同的语言发布的程序采用各自的方式部署和运行,对安全性,隔离性,跨平台等方面都会各自面临相同的问题,而没有统一的机制解决。

  • 再次,对于开发者来说,为了满足项目中不同部分的需求,需要采用不同的语言来开发,开发者需要掌握多种不同类型的语言,技术栈负担重。

  • 最后,不同的语言是为解决特定的问题,会在其他方面做妥协(例如,脚本语言 JS , Ruby 开发者门槛低,但性能也低),对于已有模块的复用和迁移代价较大,无论是语言间相互转换( transcompile )还是重写都需要付出极大的代价。

WebAssembly 作为一个可安全隔离,高效,体积小、跨平台,多语言支持的可移植二进制中间形式,可以很好的为解决如上几方面的问题提供可行的路径,不同的开发者可以根据需要利用 WebAssembly 的某一个或多个特性来满足业务需求。

41483805b199e4ceb0f1af5dff89e0ee.png7d17bf446974b2ab8818586d468bb678.png

https://www.youtube.com/watch?v=fh9WXPu0hw8

如上图所示,对于 JS/Python 等脚本语言来说,为了追求更高的性能,可以将性能热点模块通过 WebAssembly 来实现,从而获取高性能执行的收益。

对于 Rust 开发者来说,利用语言的特性可以获取高性能和高安全性,但为了让开发者获得更低的开发门槛,可以编译为 WebAssembly 模块提供给类似 JS , Python 等脚本语言使用,降低开发者门槛;

对于 C++ 开发者来说,可以获得高性能,但 C++ 不完备的安全性机制可能会使应用存在安全隐患,可以将其编译为 WebAssembly 在轻量级安全沙箱中运行,从而使得安全机制做到开箱即用(安全性保障需要安全领域的专业支持,门槛很高)

WebAssembly 生态包括开发者(开发语言),编译工具链,利于高性能执行和体积小的WebAssembly Binary 标准格式,与各种编程语言友好集成机制( binding ),以及高性能跨平台的执行引擎组成。

dd5c44f9239e1188bf7108c5d3c5fdb6.png

WebAssembly Languages && ToolChains

https://github.com/appcypher/awesome-wasm-langs

4c0ecf28f4bea8d8542c9c92ee4c7c4d.png

WebAssembly 为了良好的开发者生态,支持越来越多的语言编译成  WebAssembly 。语言的支持程度取决于各自编译器的编译工具链的成熟度,如下部分将梳理相对成熟的几个可以编译成 WebAssembly 的语言及其编译工具链情况。

1.1 C/C++

WebAssembly 已经作为 LLVM 的一个默认支持的后端,通过 Clang 可以将 C/C++ 代码直接生成 WebAssembly 目标程序(https://zhuanlan.zhihu.com/p/339950505);

Emscripten 工具链主要提供了常用的 C/C++ 库( sys-libs ),并在 LLVM 的基础上集成了Binaryen 的模块,对 WebAssembly 做进一步优化同时生成 JavaScript API 以便于宿主环境通过 JS 接口集成 WebAssembly 代码。

147de2c99e42424535bafd05fda07870.png

sys-libs: https://github.com/emscripten-core/emscripten/tree/main/system/lib

Emscripten: https://github.com/emscripten-core/emscripten

Binaryren: https://github.com/WebAssembly/binaryen

standalone WebAssembly: https://v8.dev/blog/emscripten-standalone-wasm

1.2 Rust

Rust 语言是基于 LLVM 后端实现的编程语言。在编译器层面来说,Rust 编译器仅仅是一个编译器前端,它负责从文本代码一步步编译到LLVM中间码(LLVM IR),然后再交给LLVM来最终编译生成机器码或 WebAssembly 代码,所以对于 WebAssembly 代码生成利用的是LLVM编译后端。

Rust 生态中,Cargo 和 wasm-bindgen 可以自动化编译过程,生成 WebAssembly 的主语言绑定代码,大大简化了 WebAssembly 模块的生成和使用成本。

a3146903fb729a0e598bf061b22a10ac.png

1.3 GO

TinyGo 是基于 LLVM 后端实现的 Go 编译器。在编译器层面来说,TinyGo 仅仅是一个编译器前端,它负责从文本代码一步步编译到LLVM中间码(LLVM IR),然后再交给LLVM来最终编译生成机器码或 WebAssembly 代码(编译器结构参照 Rustc )。

Go compiler for small places. Microcontrollers, WebAssembly, and command-line tools. Based on LLVM. https://github.com/tinygo-org

1.4 Java

Java 是基于类的、面向对象的一种通编程语言。Java 以类为单位将程序编译为字节码在虚拟机上运行,从而实现“编写一次,随处运行”(WORA)。

  • TeaVM -  Java 字节码的静态编译器(AOT),它生成的 JavaScript 和 WebAssembly 可以在浏览器中运行。TeaVM 不需要源代码,只需要编译好的类文件,所以 TeaVM 可以处理任何可编译为 Java 字节码的语言,例如 Kotlin , Scala 等。https://github.com/konsoletyper/teavm

  • Bytecoder - 将 JVM 字节码解析并转换为中间表示。该中间表示通过优化器阶段传递并发送到后端实现以生成目标代码,其中 WebAssembly 后端( LLVM etc.)将中间表示转换为 WebAssembly 文本和二进制代码。https://github.com/mirkosertic/Bytecoder

  • CheerpJ - 是基于 LLVM 的 Java 字节码到 WebAssemlby/Javascript 的 AOT 编译器。可用于将Jar或单个.class 文件转换为WebAssembly或者JavaScript。https://github.com/leaningtech/cheerpj-meta

  • JWebAssembly 将 Java 字节码编译为 WebAssembly 。它使用 Java 类文件作为输入,可以编译任何可以编译为 Java 字节码的语言,例如 Clojure、Groovy、JRuby、Jython、Kotlin 和 Scala。作为输出,它生成二进制格式(.wasm 文件)或文本格式(.wat 文件)。目标是使用 WebAssembly 在浏览器中本地运行 Java。与类似项目的不同之处在于,不需要移植具有完整 GC 和内存管理的完整 VM 。它更像是 1:1 的转换。生成的 WebAssembly 代码与原始 Java 类文件的大小相似。

https://github.com/i-net-software/JWebAssembly

1.5 Javascript/TypeScript/AssemblyScript

1.5.1 Assemblyscript

https://github.com/AssemblyScript/assemblyscript/blob/main/src/README.md

https://github.com/WebAssembly/binaryen

AssemblyScript 是面向 WebAssembly 标准的严格类型化的 TypeScript 变体,通过 Binaryen 后端编译为 WebAssembly 。如下图所示,TypeScript 和对应的 lib 库通过编译器前端生成AssemblyScrip t,并集成 Glue 代码片段利用 Binaryen 后端生成 WebAssebmly Module。

688b12828b6e7ef93b56fd44863e2411.png

  • AssemblyScript 是面向 WebAssembly 标准设计和演进,随着 WebAssembly 2.0 标准演进(类型和指令扩展),AssemblyScript 也向着"通用"的方向演进(类型系统扩展,语法扩展)

  • AssemblyScript 可以通过 TS 标准库的方式扩展能力,在其 low-level 低级功能(类型系统)之上提供类似 JavaScript 的"通用"标准库等。

(https://www.assemblyscript.org/introduction.html#from-a-javascript-perspective)

1.5.2 Assemblyscript  < XScript < TypeScript(JavaScript)

XScript 用于代表一类介于 Assemblyscript 和 TypeScript 之间的语言集,其语法和易用性类似于 TypeScript ,但它属于静态,强类型语言,可以利用通过静态编译来提升性能。

StaticScript 是一门 TypeScript 语法兼容的静态编译型语言,其基本设计思路是通过"受限类型系统来约束和指导静态编译(https://staticscript.org/guide/type),其编译流程如下图所示:

7a04ba55c02175f056d58b3fca8cbb2e.png

StaticScript设计目的和AssemblyScript类似(静态编译),但采用的路径有所差别,AssemblyScript 类似于原始 WebAssembly 的一层 thin layer 语言层封装;而 StaticScript 是从类型系统着手支持静态编译。(完备的类型系统需要考虑语言的图灵完备性,类型系统的soundness 等众多方面,具有一定的难度和复杂度)

  • StaticScript: https://github.com/ovr/StaticScript

  • StaticScript

    • https://staticscript.org/guide

    • https://github.com/StaticScript/StaticScript

ts2wasm

除了自定义语言外,另外一个方案是基于 TypeScript 语言,扩展和定制编译工具链以编译生成 WebAssembly 代码。

f34cd540a1214ad140b04735f5f37564.png

  • WebAssembly Open Day: https://www.sofastack.tech/activities/sofa-meetup-13/

  • 蚂蚁集团 WASM 编译器虚拟机基础能力建设 : https://www.bilibili.com/video/BV1NF411B79W/

1.5.3 JavaScript

JavaScript 是一种符合 ECMAScript 规范的动态、弱类型、基于原型和多范式的语言,其性能,安全隔离性等方面受到广泛的关注。由于 JavaScript 的弱类型和动态性,在降低开发者门槛的同时也为静态分析,静态编译,性能优化带了不小的阻碍;为此,社区进行了广泛而深入的研究,我们尝试从如下基本来做一个初步的梳理和讨论。

(1) 性能敏感模块 WebAssembly 化

为了提升基于 JavaScript 应用性能,V8 等虚拟机引入了 JIT 机制。JIT Pipeline (如下所示)首先需要将 JavaScript 编译为后端代码,在应用启动性能,内存占用上都会存在损耗,而为了快速启动的解释执行相比较 WebAssembly 来说也存在一定的性能差异,因此,对于 CPU 密集型计算(例如处理数学,图像处理)等任务可以利用 WebAssembly 来进行优化;此外 wasm 可以大大减少移动设备上的电池消耗(取决于引擎),因为大部分处理步骤在编译期间已经提前完成(基于 JavaScript 字节码发布或者缓存可以达到类似效果,但 JavaScript 字节码没有统一的规范和标准,无法作为通用产物分发)。

a369000a5bd34bdebc7a6b140482fa1f.png

https://blog.sessionstack.com/how-javascript-works-a-comparison-with-webassembly-why-in-certain-cases-its-better-to-use-it-d80945172d79

(2) Javascript AOT Engine

JIT 可以极大的提升 JavaScript 性能,禁用 JIT 的 V8 可能比启用 JIT 的 V8 慢 x5~x17 倍,因此JIT对于实现可接受的帧率至关重要。而对于受限硬件,特殊系统平台(iOS)大多数控制台不允许用户应用程序在运行时创建可执行代码,从而有效地消除了使用 JIT 的可能性。

在无法使用JIT的情况下,要运行 JavaScript 有如下3个可能的方式:

  • 在JS虚拟机的解释模式下执行 JavaScript

  • 离线编译和优化 JavaScript 代码,如采用静态编译或 AOT 编译,将部分任务提前到运行前完成。

  • 用性能友好的语言(如 C/C++ 、Rust )手动重写。

以上三种方案中,解释执行的模型在性能上无法满足需求;而用新语言重写项目无论是通用性和规模效应都是无法接受的。那么唯一的选项就是 AOT (静态编译)。而 JavaScript 是弱类型的动态语言,传统的静态编译需要明确获取类型信息,似乎无法实现 AOT ,那么提升JavaScript 的"银弹"是什么呢?

"静态编译的基本原则是封闭性假设( closed world assumption ),要求编译器在编译时必须掌握运行时所需的全部信息,换句话说,就是运行时不能出现任何编译时未知的内容"。从以上的定义其实我们可以看出,并没有严格定义动态类型不能进行 AOT ,它要求的是需要在编译期做到"封闭性",也就是说所有的编译期需要分析和处理所有可能性,在运行期间出现的内容必须是编译期内容的"子集(或真子集)"。

JavaScript AOT compilation: https://hal.archives-ouvertes.fr/hal-01937197/document

如果换一个角度来讲,JIT 机制利用 profile 的数据将部分热点函数通过编译过程转换为高效的机器码进而提升性能,而 AOT 则通过一定的机制和假设(speculative optimization)将 JIT 过程提前到编译期完成。

下面主要基于如下几个假设简单阐述 AOT 的相关流程:

假设一:

大多数 JavaScript 程序在启动阶段后表现出相当静态的行为(这也是JIT的基础)。一旦应用加载完成并完成"预热后"(初始化),对象的原型链很少会发生变化(数据结构,继承关系,大多数全局变量结构也很少会改变。

在AOT过程中,静态编译器可以充分利用这一假设,ChowJS 大致过程如下图所示。

0ed339b3c0f199ae6b0a0a68894532b6.png

首先,ChowJS 解释器加载 JavaScript 运行,在应用启动阶段完成后,对应的方法均已编译为字节码。

其次,对每个编译为字节码的函数(F)

  • 将F的字节码转换为 SSA IR 。

  • 在 IR 上执行多个 Passes 和优化。

  • 将 IR 转换为 C 并将其编译为机器代码。

对于 AOT 编译,我们必须能够访问启动 JS 的上下文,因为这使得用户对象、原型和方法在编译时可供我们使用,并使优化更加直接。详细过程访问如下链接:

  • ChowJS:an AOT JavaScript engine for game consoles: https://mp2.dk/techblog/chowjs/

以上重点讨论了 JavaScript 静态编译的可行性及流程,能够静态编译为 C 代码,那么就能够顺利的静态编译为 WebAssembly 。因此,对于可 AOT 的部分,生成 WebAssembly 模块也是一个可选项。

假设二:

即使在看到实际 JS 代码之前,我们就可以预见到需要生成一系列的 IC stubs ,因为 JS 中会经常使用到一些固定的模式编程模式。一个很好的例子是访问对象的属性。这在 JS 代码中经常发生,并且可以通过使用 IC Stub 加速访问。对于具有特定“形状”或“隐藏类”(即以相同方式布置的属性)的对象,当从这些对象获取特定属性时,该属性将始终处于相同的偏移量。虽然在 JS 运行前无法获取实际的类型信息,但可以 “parameterize IC stub” 及生成属性访问的函数模板,并且将类型信息和属性偏移作为参数传递给 IC stub 。

经测试发现,仅需几 KB 的 IC stubs 就可以覆盖绝大多数 JS 代码。例如,使用 2 KB 的 IC stubs ,我们可以覆盖 Google Octane 基准测试中 95% 的 JS 代码,详细内容见下图及 "Fast AOT-compiled JS" 链接所示。

Fast AOT-compiled JS: https://bytecodealliance.org/articles/making-javascript-run-fast-on-webassembly

7f82b17146f0f7b0ccf98d1433d57852.png

如前所述,JavaScript 是动态弱类型语言,我们可以通过一定的机制对热点做 AOT ,但纯静态编译需要遵循"封闭性"原则,对于静态特性之外的部分,需要通过 Fallback 机制进行兼容,从而满足"封闭性假设"(deopt);Fallback 机制根据不同的设计有多种不同的实现,例如基于 AST 的解释执行,基于 Bytecode 的解释执行,基于未优化的机器码执行等方式。这意味着实际引擎会有几个执行层:

  • AST:通过遍历 AST 树解释执行

  • 字节码:通过解释器运行的字节码( bytecode 预处理)

  • 未优化的机器码:直接从字节码生成的机器码。这是通过为每个字节码指令内联操作码处理程序来完成的,从而消除了操作码调度开销。我们还在这里执行一些有限的窥视孔优化。这类似于 V8 的 Sparkplug 的工作方式(copy threading, etc.)。

  • 优化机器码:AOT生成的优化字节码,如前所述。

SideNote:

  • Hermes 是唯一用于生产环境的 JS AOT 编译器,不过它只编译为字节码而不是机器码,并且它不支持 JS 的所有能力(non full feature)。

  • ChowJS 很大程度上基于 QuickJS ,它是一个优秀的 JavaScript 引擎。ChowJS 借用了 QuickJS 的字节码、解释器和 ECMAScript 支持。

  • Fastly 最近宣布了 JavaScript AOT 支持,但目前它似乎只在 Wasm 中实现了一个 JS 解释器(SpiderMonkey)。

(3) WebAssembly 沙箱运行 JavaScript

WebAssembly 的轻量部署,高效冷启动和进程级安全沙箱特性,对于应用快速启动和安全隔离具有重要的作用。如果该语言无法编译为 WebAssembly ,它将如何在 wasm-runtime 上运行?其中一个可行的方案是将语言的运行时编译为 WebAssembly ,而不是将程序本身编译为 WebAssembly 。

b2d87e2a5f88f068f86c3413e66a222d.png

但这个又带来了另外一个性能问题,因为 WebAssembly 不能使用 JIT ,不允许动态生成新的机器代码并在纯 Wasm 代码中运行,而只能使用解释器。

c6ee16f8b5841477c15c6a93f713670c.png

经调查发现,JS 运行过程可以大致分为初始化阶段和执行阶段,而初始化阶段可以在 AOT 过程运行,并将初始化后的虚拟机状态保存为 snapshot 数据。当实际执行应用时,可以加载 snapshot 而跳过初始化环节,从而大大提升应用启动性能(详细内容请访问如下链接内容)。

Making JavaScript run fast on WebAssembly: https://bytecodealliance.org/articles/making-javascript-run-fast-on-webassembly?_fsi=UYShxlrT

Hit the Ground Running: Wasm Snapshots for Fast Startup: https://www.youtube.com/watch?v=C6pcWRpHWG0&t=1338s

此外,我们可以采用 JavaScript 的 AOT 机制来优化 JavaScript 代码,以提升 wasm 引擎的吞吐量。

WebAssembly Runtimes

WebAssembly 是轻量,跨平台的标准二进制指令格式,基于 WebAssembly 可以实现“编写一次,随处运行”(WORA)。随着 WebAssembly 不断发展,并不断发展成为一个成熟的平台。实现 WebAssembly 运行时变得更具挑战性和耗时,各 WebAssembly 运行环境逐渐选择构建自己的生态系统,如 wastime 、Wasmer、V8、Lucet、WAMR 等。

更多内容见如下链接: https://github.com/appcypher/awesome-wasm-runtimes

2.1 wasmtime(Bytecode Alliance project with Rust)

https://github.com/bytecodealliance/wasmtime

Wasmtime 是一个字节码联盟项目,用于在非 Web 环境执行 WebAssembly 和 WASI 平台绑定。

Wasmtime 是由非盈利组织 Bytecode Alliance 主导,其成员是 WebAssembly 的主要推动者(核心成员来自 mozilla Rust+Wasm 团队,核心代表任务是 lin clark 及其团队成员),因此,在 Wasm Spec 的演进,新特性实现,Wasm 工具链,Wasm 生态建设方面,wasmtime 将是最佳的试验场。

https://bytecodealliance.org/articles/1-year-update

Note: The Bytecode Alliance doesn’t host specifications. While BA members are driving specs mentioned below, they are doing that in collaboration with others in the W3C WebAssembly CG. Bytecode Alliance projects include implementations of these specs.

Wasmtime 支持了 WebAssembly 的标准功能,但当前强依赖自研的 Cranelift 编译后端而不支持 LLVM ,所以目前仅在 x86_64 上保证可用并且性能比 llvm 要差很多(详见性能对比),另外,对其他平台的支持还在进行。“The x86-64 backend is currently the most complete and stable; other architectures are in various stages of development.”

wasmtime 包括如下核心模块

  • Wasmtime : wasmtime runtime 作为模块集成暴露的安全公共 API

  • Wasmtime-runtime: wasmtime low-level 实现实现,包括管理模块的执行上线文 VMContext 和 InstanceHandle 等。理想情况下对于 JIT 代码如何生成和实际的执行引擎是无感知,当前对于两者都有一定程度的依赖。

  • Wasmtime-jit: 使用 JIT 方式执行 WebAssembly 的模块,包括使用 Cranelift 编译,wasmtime JIT 生成的可执行程序等。当前所有的模块都通过 JIT 方式编译,未来 wasmtime 可能采用非 JIT 的方式加载预编译的模块。

  • Wasmtime-environ: 编译后端的统一接口层,用于 Wasmtime 集成不同的编译后端的统一抽象层。移除所有编译后端的依赖,让 wasmtime-environ 成为一个相对独立的 crate  是最终目标,但目前 cranelift 是 wasmtime 的一个依赖,大部分的 wasmtime-environ 的导出符号在其 API 中用了 cranelift 的类型。

  • Wasmtime-cranelift:Wasmtime 函数级 WebAssembly 编译器。

详细内容参见: Architecture of Wasmtime: https://docs.wasmtime.dev/contributing-architecture.html#architecture-of-wasmtime

2.2 wasmer (with Rust)

https://wasmer.io/

wasmer 由 Wasmer Inc. 开发并开源的高性能且安全的 WebAssembly 运行时,提供能够在从桌面到云、边缘和物联网设备等随处可运行的超轻量级容器。

wasmer 面向商用场景着力打造应用生态,用户体验,极致性能,提供了通用、完善的功能,丰富的文档、社区,论坛、博客,广泛的语言和工具(wapm)支持,其最大的好处是"开箱即用"。

https://docs.wasmer.io/ecosystem/wasmer/wasmer-features

https://docs.rs/wasmer/2.0.0/wasmer/

f65f508dccc8c655a4a7b20cd7a66332.pnga42e9694aefcbc5938c0e29350e7d6f1.png

正如 wasmer 所宣传的

"Wasmtime did the basics. Wasmer added your language and skyrocketed speed."

https://wasmer.io/wasmer-vs-wasmtime

2.3 WasmEdge(A cloud native WebAssembly runtime with C/C++)

https://github.com/WasmEdge/WasmEdge

WasmEdge 的前身是 SSVM (SecondState VM) ,主要应用于"应该使用 Docker 而 Docker 用不起来的地方",即把云原生这套理念和工具应用到边缘计算场景。

A Cloud-native WebAssembly Runtime: https://www.youtube.com/watch?v=9LpvgWaG_T0

WasmEdge 成为 CNCF Sandbox 项目: https://toutiao.io/posts/m7p2v10/preview

ed5591d87e9a048f2e473a7590e39c63.png

如上图所示,WasmEdge 最大的特点是将执行引擎融入了 k8s(Kubernetes) 的云原生框架中,为云原生的执行增加了高性能,轻量的 WebAssembly 执行引擎(基于 LLVM AOT 优化)。

2.4 wamr(Bytecode Alliance project with C/C++)

WebAssembly Micro Runtime (WAMR) 是一个轻量级的独立 WebAssembly 执行引擎,非常适合资源极其有限的小型嵌入式设备(占用空间小,使用解释器来保持较低的内存开销具有占用空间小)、此外,也可用于从嵌入式、物联网、边缘到可信执行环境 (TEE)、智能合约、云原生等的应用程序。

WAMR 项目包括以下三部分功能:

  • iwasmVM 内核,是 WASM 字节码的执行引擎,支持解释器、提前编译 (AoT) 和实时编译 (JIT)多种模式;

  • WASM 应用程序API和多实例应用框架;

  • WASM 应用程序的动态管理;

https://github.com/bytecodealliance/wasm-micro-runtime

https://www.w3.org/2020/08/29-chinese-web/wamr.pdf

https://gw.alipayobjects.com/os/bmw-prod/dfadad7f-b3f0-48e7-b7b0-14a4fad65efc.pdf

2.5 V8(with C/C++)

V8 是 Google 开发并开源的高性能 JavaScript 引擎,同时也能够运行 WASM 模块。为了保证 WebAssembly 一个可预期的启动和执行性能,V8 专门为 WebAssembly 加入了一个全新的 WebAssembly baseline 编译器-Liftoff 。Liftoff 的目标是通过尽可能快地生成代码来减少基于 WebAssembly 的应用程序的启动时间。代码质量是次要的,因为最终使用 TurboFan 重新编译热代码。Liftoff 避免了构建 IR 的时间和内存开销通过 WebAssembly 函数的字节码一次性生成机器代码。

b78baf65481d2e07a3652784c9d52c61.png

有了 Liftoff ,现在 V8 引擎针对 WebAssembly 有了两个编译层级:Liftoff 作为 baseline 编译器提供快速启动的能力,TurboFan 作为优化编译器提供最佳性能。这就带来了一个问题,如何协调使用这两个编译器以带来全局最佳的用户体验。V8 选择了“eager tier-up” 策略,在完成模块的 Liftoff 编译之后,WebAssembly 引擎立即启动后台线程以生成模块的优化代码。这允许 V8 快速开始执行代码(在 Liftoff 完成后),但仍然尽可能提供性能最高的TurboFan 代码。

但 V8 是专为 JavaScript 优化的,无法独立 WebAssembly standalone Runtime ,而和JavaScript 引擎融合在一起,导致体积过大,并且在 WebAssembly 的执行过程中冷启动时间较长。此外,V8 的隔离是通过使用 Isolate 实现的,而在云原生场景下,V8 无法提供进程级安全隔离性。

对于执行引擎,性能是最重要的衡量指标之一。Libsodium 长期以来一直以 WebAssembly 为目标,其内置 benchmark 测试集合可以在各种独立的 WebAssembly runtime 上运行。

从如下的测试结果可以看出:

  • WAVM 仍然是最快的 WebAssembly 运行时,在每一个测试中都击败了竞争对手(但对WebAssembly 的生态支持有限)。WAVM 基于 LLVM,但其性能要比是同样基于 LLVM bankend 的 wasmer 快 15% 左右。

  • V8 (nodejs) 成为第二名的表现非常令人惊讶。WAVM 和 wasmer-llvm 会提前编译所有内容,花时间应用尽可能多的优化。V8 有两个 WebAssembly 编译器:LiftOff(singlepass编译器)和 TurboFan(应用更积极优化的大规模并行编译器)。该组合提供即时启动。然后,不会立即达到最佳速度,但启动 TurboFan 后取得了很大的优化效果。

  • singlepass wasmer 性能表现也很好。它的性能几乎与 JIT 后端一样(重复测试的结果保证测试的有效性)。singlepass 的编译速度也非常快。

  • 采用 cranelift 的 wasmtime 性能标准也非常好。经过和 lucet 的对比实验,发现性能的提升不是由于 cranelift 编译器获取的,而是其他 wasmtime 实现收获。cranelift 一个非常年轻的代码生成器,从头开始编写,它的性能与基于 LLVM 的运行时存在一定的差距。

c1c1104dc17fc6d53a67431b610676d0.png

https://github.com/jedisct1/webassembly-benchmarks/tree/master/2021-Q1

对于开源项目的选型并非是非此即彼的选择题,特别是 wasmtime 和 wasmer 都是基于 Rust ,有很多的共同基础,如果能够基于两个引擎的优势做一定程度的相互融合,一方面可以利用两个社区的力量,同时也可以根据实际的业务场景进行有效整合,也许是一个值得尝试的引擎演进路线。

wasmer 的通用、完善的功能,丰富的文档、社区,论坛、博客,广泛的语言和工具支持,对于对实际的业务提供更好的支持,可以作为业务侧生态建设的参考标准。

wasmtime 紧跟标准演进,可以提供最新的特性和能力,此外,从上述的性能分析可以看出基于 LLVM backend 的编译器可以获取较高的性能,LLVM backend 是一个全球开发者共建的高性能代码生成引擎,cranelift 之外,wasmtime 借鉴 wasmer 采用 LLVM backend 作为代码生成器也许是一个可以考虑的融合方式。

WasmEdge 是一个基于 LLVM 的运行时,专注于性能,仍然是一个年轻的项目,目前仅支持 Linux/x86_64,可能还不是很稳定,可以持续关注。

Relation to wasmtime: https://github.com/wasmerio/wasmer/issues/142

ActuallyUsingWasm: https://wiki.alopex.li/ActuallyUsingWasm

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

b722df98d52a0e6966bed886adebc38b.png

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值