WebAssembly初识

WebAssembly简介

下面是WebAssembly官方文档给的说明:

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是一个基于栈的二进制指令格式,被设计为编程语言可移植的编译目标,支持部署到web客户端和服务端应用",也就是WebAssembly通常被当作其他编程语言的编译目标 上面的介绍比较太宽泛,下面是WebAssembly核心规范给的定义:

WebAssembly (abbreviated Wasm ) is a safe, portable, low-level code format designed for efficient execution and compact representation. Its main goal is to enable high performance applications on the Web, but it does not make any Web-specific assumptions or provide Web-specific features, so it can be employed in other environments as well.

大致意思就是"WebAssembly(简称为Wasm)是一个安全、可移植的低级代码格式,被设计成高效执行和紧凑表示。它的主要目标是在Web开启高性能应用,但是不做任何特定于Web的假设或提供特定Web的特性,因此它可以也可以部署在其他环境" 两个定义其实都说明了WebAssembly的一些特点,第一个是从广义的角度来讲,第二个是从核心规范的角度来讲

发展历程

WebAssembly的初衷是为了提升Web应用的性能,但是在初版发布之后,WebAssembly在开发者社区越来越流行;因此WebAssembly的潜在价值开始向其他领域蔓延,如云原生、AI以及区块链等。以下是一些关键节点:

  1. 2015年,Mozilla发布一种新型的二进制代码格式"WebAssembly"
  2. 2017年,FireFox、Chrome、Edge和Webkit四大浏览器厂商在WebAssembly MVP(最小可用版本)标准的设计达成共识;同年,WebAssembly Working Group(简称WWG)成立,标识着WebAssembly 成为 W3C 标准技术体系的一部分
  3. 2019年12月,宣布 WebAssembly 成为第 4 种 Web 语言;Bytecode Alliance 字节码联盟宣布正式成立
  4. 2022 年, WebAssembly 2.0 草案正式发布,WebAssembly 2.0 草案中加入了很多值得关注的新特性,比如引用类型(Reference Types)、固定宽度的 SIMD(Fixed-width SIMD)、批量内存操作(Bulk Memory Operations)

规范相关

WebAssembly涉及的内容主要由以下几部分组成:

  1. 核心规范:定义了独立于具体嵌入环境的WebAssembly模块语义,可以理解为Wasm的语义规范
  2. 嵌入的接口:由三部分组成:
    1. JavaScript API:定义从JavaScript访问WebAssembly的类和对象,包括用于验证、编译和实例化的方法,以及导入作为操作和代表、导出作为JavaScript对象的类。
    2. Web API:定义可用于特定浏览器的JavaScript API的扩展,如使用请求的Response作为流式编译和实例化的接口
    3. WASI API:定义了模块化的系统接口,用于Web外运行WebAssembly,包括文件系统、网络连接、锁和随机数
  3. 工具:相关工具,如连接方案、调试信息和语言ABI等
  4. 原始设计文档:WebAssembly的设计、目标和高级概述

特性相关的一些文档/链接:

  1. 特性提案被标准化的流程
  2. 特性提案列表(各个阶段的特性)
  3. 结束的提案(已经进入第四阶段)
  4. 不活跃的提案

WebAssembly有什么优势

  1. 快速、高效 :WebAssembly是静态强类型语言 ,利用常见的硬件能力,可以在不同平台上接近原生的速度运行(我理解的接近原生速度运行是使用C/C++代码编写,编译为Wasm后性能接近C/C++,也就是和原始语言的性能接近)
  2. 跨平台、可移植:WebAssembly是一个可移植、体积小,并且与语言和平台无关的二进制格式,开发者可以使用各种自身熟悉的语言开发,生成的wasm作为平台无关的发布形式
  3. 安全:WebAssembly被限制在一个安全的沙箱环境中运行,一方面可以防止恶意代码攻击,另一方面可以保证数据安全
  4. 标准化 :WebAssembly开放标准是由W3C社区组(包括所有主流浏览的代表)和W3C工作组一同制订的
  5. 灵活的开发语言:WebAssembly制定了标准化的中间指令格式,开发可以使用各自熟悉或喜欢的开发语言进行开发,如C/C++、Java/Kotlin、TypeScript和Rust等

以上这些优势其实也是WebAseembly的设计目标,WebAssembly的设计目标详情可以查看WebAssembly核心规范(设计目标) 上面的优势看起来比较官方,不那么直观,用我的理解通俗一点的解释一下:

  1. 使用支持编译为WebAssembly的语言写的代码可以跨平台运行,目前为止大部分主流语言基本都支持编译为WebAssembly,一套代码在Window、PC、Linux、Android、iPhone、物联网终端运行将成为可能
  2. 可以带来更好的性能,如使用Rust编写的代码编译为Wasm后将会有将近Rust的性能,这对于Web应用来说是质的飞跃

现状

支持编译为Wasm语言

主流语言C/C++、Go、Rust、TypeScript(AssemblyScript)、Java、Python、.Net/C#等等,完整的支持列表可见awesome-wasm-langs 目前支持情况最好的几个语言是:

  1. Rust
  2. C/C++
  3. AsssemblyScript
  4. Blazor
  5. Python
  6. Go

以上一些语言的详细信息可以查看【字节跳动】WebAssembly常用开发语言和工具链 针对开发WebAssembly应用程序的常用语言,有机构进行了相关的调查,近两年的调查结果如下: 相关资料The State of WebAssembly 2021 相关资料The State of WebAssembly 2022

编译器

常见的编译器有如下几个:

  1. Emscripten:可以将C/C++代码编译为WebAssembly
  2. wasi-sdk:提供了一个基于LLVM更为纯净将C/C++编译为WebAssembly的编译器,该项目主要仅提供WASI库,其他编译功能主要基于LLVM
  3. TingGo:基于LLVM将Go编译为WebAssembly的轻量编译器,可以在嵌入式场景使用
  4. wasm-pack:将Rust代码编译WebAssembly并打包成npm模块
  5. wasm-bindgen:一个Rust工具,用来将JavaScript能力导入到Rust或将Rust能力到处到JavaScript(这不算一个编译器,但在Rust生态经常和wasm-pack一起使用)
  6. AssemblyScript:将类TypeScript的AssemblyScript代码编译为WebAssembly的编译器
  7. Binaryen:与语言无关的一个通用的WebAssembly优化器
  8. 等等

详细信息可以查看【字节跳动】WebAssembly常用开发语言和工具链

引擎

有关于引擎的详细信息可以查看【字节跳动】WebAssembly 常见引擎简介

wasmtime

仓库地址github.com/bytecodeall... 是Bytecode Alliance(字节码联盟)推出的拳头项目之一,wasmtime对于WebAssembly相关标准支持的完善度非常高。它支持了标准的WASI(WebAssembly System Interface),并紧密跟踪WebAssembly核心特性。目前 Fixed-Width SIMD、Reference Types、Bulk Memory operations成熟提案, 以及Tail-Call、Threads 和 Garbage Collection 等还处于标准实现阶段的提案都已经被支持。 支持的语言:Rust(wasmtime的实现语言)、C/C++、Python、.NET、Go等

wasm3

仓库地址github.com/wasm3/wasm3 wasm3是一款解释执行的轻量级WebAssembly引擎,使用C语言编写。最低可用系统要求:64kb的存储空间和10kb的内存空间。再加上wasm3是纯解释执行,可以在iOS等设备上运行,这一点其他纯编译型引擎无法做到的,使跨平台具有可行性。但是目前wasm3对WebAssembly规范的支持度一般,具体情况如下: 支持的语言:Rust、C/C++、Python3、.NET、Go等

WasmEdge

仓库地址github.com/WasmEdge/Wa... WasmEdge是一款由CNCF(Cloud Native Computing Foundation,云原生计算基金会)托管的WebAssembly引擎,从它的名称也可以看出,WasmEdge主要面向边缘计算、云原生和去中心化应用。和wasmtime一样,WasmEdge也是编译型的wasm引擎,可以按照JIT/AOT两种模式对wasm指令进行编译,并执行。不同之处在于WasmEdge使用LLVM作为编译器后段,利用了LLVM出色的优化编译能力。因此,相比于使用Cranelift的wasmtime,WasmEdge生成的指令更优,执行速度更快。Wasm支持所有标准的WebAssembly特性和大量扩展提案。 支持的语言:Rust、C、Go等

V8

上面几款引擎都是纯粹的WebAssembly引擎,专用于wasm程序的执行,通常在standalone的场景中使用。但是WebAssembly是由四大浏览器厂商联合推出的,最初在Web环境中使用,JavaScript引擎才是wasm最早的执行引擎。 V8 执行 WebAssembly 也是使用编译后执行的方式: 通过自身的 TurboFan 或者 LiftOff 编译后端,进行 JIT 编译后执行。根据一份 2021 年的测试数据,NodeJS 在 Benchmark 上的性能优于 wasmtime,可见 V8 的 性能足以媲美多数专门的wasm引擎。 在标准支持方面,考虑到 V8 一般在 Web 或者 NodeJS 环境中运行 wasm 程序,所以 V8 并不支持 WASI 标准。但是 Chrome & V8 团队作为 wasm 标准制定的主要参与者之一,V8 引擎对 WebAssembly 的 JS API、MVP 以及 Post-MVP 等核心提案都完整支持,并且实验性地支持各类处在探索阶段的提案。因此,如果想要尝试 WebAssembly 的最新提案实现,V8 引擎是一个不错的选择。 支持的语言:C++ WebAssembly官网有一个特性表格,列出了各引擎对特性的支持情况,可以查看你的浏览器支持哪些特性:webassembly.org/roadmap/

调试

目前WebAssembly对以下几种调试方式支持较好:

  1. 使用Chrome的Devtool进行调试,具体如何调试可以查看Chrome开发者工具的文档,支持C/C++、AssemlbyScript等语言编译为WebAssembly调试
  2. 原生调试,这种方式其实不是在调试WebAssembly代码,而是将WebAssembly反编译为原始代码,然后调试原始代码
  3. lld+wasmtime调试:借助 lldb 和 wasmtime 的能力,将 wasm 的调试信息在 JIT 编译时同步转换到 native 格式,可以获得非常接近于原生调试的体验

前端开发建议首选Chrome+Chrome插件的方式,和平时开发使用source面板基本类似,示例如下:

使用场景和未来趋势

使用场景

  1. Web环境:视频解码和GUI控件(爱奇艺的解码)、浏览器平台的虚拟实现(VR)、游戏相关(Doom 3)、数据库、物理引擎等等
  2. 云原生:2022年Docker宣布推出与WASM集成的首个技术预览版、Krustlet(用于将 Kubernetes 管理的工作负载交付给 WASM 运行时的工具,实现的是Kubelet的功能)
  3. 边缘计算
  4. 移动设备、IoT和物联网
  5. 区块链技术

未来趋势

  1. 更好的开发体验
  2. 标准继续推进
  3. 理念、传播和社区

WebAssembly支持的格式

二进制格式

WebAssembly的二进制格式通常使用wasm扩展名,通常使用其它语言编译为WebAssembly的产物就是该格式,是一种紧凑的、机器可度的二进制形式,这也意味着该格式是人为不可读的。 Web浏览器或其他支持WebAssembly运行时环境加载和执行的格式就是该格式,该格式在编译器中呈现的形式可能类似如下:

文本格式

为了能够让人类阅读和编辑 WebAssembly,WebAssembly提供了相应的文本表示。这是一种用来在文本编辑器、浏览器开发者工具等工具中显示的中间形式。相对于二进制格式,文本格式可读性较高,但基本上和汇编语言一个层级,通常使用wat 作为文件后缀(两种格式是可以互转的,在不包含其他语言特性的情况下可以使用wabt工具集的wasm2wat和wat2wasm命令互相转换),wat文件内容类似如下:

运行一个简单的程序

下面介绍一个简单的例子,如何在V8引擎中(浏览器)运行一个wasm模块(这里以AssemblyScript作为WebAssembly的原始开发语言) 在开始之前先介绍一下WebAssembly的一些关键概念,这些概念都一一映射到Web Assembly的JavaScript API中:

  1. 模块:表示一个已经被浏览器编译为可执行机器码的 WebAssembly 二进制代码。一个模块是无状态的,并且像一个二进制大对象(在前端中的就是Blob)一样能够被缓存到IndexedDB中或者在 window 和 worker 之间进行共享。一个模块能够像一个 ES2015 的模块一样声明导入和导出。
  2. 内存:ArrayBuffer,大小可变。本质上是连续的字节数组,WebAssembly 的低级内存存取指令可以对它进行读写操作。
  • 表格:带类型数组,大小可变。表格中的项存储了不能作为原始字节存储在内存里的对象的引用(为了安全和可移植性的原因),如函数。
  • 实例:一个模块及其在运行时使用的所有状态,包括内存、表格和一系列导入值。一个实例就像一个已经被加载到一个拥有一组特定导入全局变量的 ES2015 模块。
  1. 配置AssemblyScript编译环境(省略),编写AssemblyScript代码(以下就是一个简单的add函数)
typescript 复制代码
export function add(a: i32, b: i32): i32 {
  return a + b;
}
  1. 编译AssemblyScript到WebAssembly,AssemblyScript编译成WebAssembly后,除了生成wat、wasm文件外还是生成一个js文件,这个文件包含了一些胶水代码,该例子的js文件内容如下:
typescript 复制代码
async function instantiate(module, imports = {}) {
  // 示例化wasm,返回一个resolve一个WebAssembly实例的Promise
  const { exports } = await WebAssembly.instantiate(module, imports);
  // wasm导出内容
  return exports;
}
export const {
  memory,
  add,
} = await (async url => instantiate(
  await (async () => {
    try {
      // 浏览器环境(这里包含两步:加载wasm、编译wasm为机器码),编译完生成一个模块
      return await globalThis.WebAssembly.compileStreaming(globalThis.fetch(url));
    } catch {
      // 报错使用Node环境兜底
      return globalThis.WebAssembly.compile(
        await (
          await import("node:fs/promises")).readFile(url)
      );
    }
  })(), {
  }
))(new URL("release.wasm", import.meta.url));
  1. 加载wasm、编译wasm、实例化wasm
  2. 在HTML文件中加载该js文件(需要使用ES模块进行加载,因为使用了export),HTML文件代码如下:
html 复制代码
<!DOCTYPE html>
<html lang="en">

  <body>
    <script type="module">
      import { add } from "./build/release.js";

      console.log("wasm add: ", add(34, 26));
    </script>
  </body>

</html>

运行结果如下:

参考资料

  1. WebAssembly官方文档
  2. 【字节跳动】走进WebAssembly世界系列课程
  3. 【MDN WebAssembly教程】WebAssembly Web开发教程
相关推荐
正小安24 分钟前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch2 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇3 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho4 小时前
【TypeScript】知识点梳理(三)
前端·typescript