WebAssembly 快速指南



WebAssembly - 概述

WebAssembly 是一种用于 Web 的新型计算机编程语言。WebAssembly 代码是一种低级二进制格式,与 Web 兼容,并且可以轻松地在现代 Web 浏览器中运行。生成的​​文件大小很小,并且加载和执行速度更快。您现在可以将 C、C++、Rust 等语言编译为二进制格式,并且它可以在 Web 上像 javascript 一样运行。

WebAssembly 的定义

根据 WebAssembly 的官方网站(可在 https://webassembly.net.cn/ 获取),其定义为 WebAssembly(缩写为 Wasm)是一种基于栈的虚拟机的二进制指令格式。Wasm 被设计为 C/C++/Rust 等高级语言的便携式编译目标,使客户端和服务器应用程序能够在 Web 上部署。

Web Assembly 不是开发人员必须编写的语言,而是用 C、C++、Rust 等语言编写的代码,可以编译成 WebAssembly (wasm)。相同的代码可以在 Web 浏览器内部运行。

Web Assembly 是一种新的语言,代码是低级汇编语言,但凭借其文本格式功能,代码具有可读性,并且如果需要,可以进行调试。

WebAssembly 的目标

WebAssembly 的开放标准是在一个 W3C 社区小组中开发的,该小组包括来自所有主要浏览器的代表以及一个 W3C 工作组。

WebAssembly 的主要目标如下所示 -

  • 更快高效且可移植 - WebAssembly 代码旨在利用可用的硬件在不同的平台上更快地运行。

  • 易于阅读和调试 - WebAssembly 作为一种低级汇编语言,支持文本格式,允许您调试代码中的任何问题,并在必要时重写代码。

  • 安全性 - WebAssembly 在 Web 浏览器上运行是安全的,因为它会处理权限和同源策略。

WebAssembly 的优势

以下是 WebAssembly 的优势 -

  • 在现代浏览器上运行 - WebAssembly 能够在可用的现代 Web 浏览器上毫无问题地执行。

  • 支持多种语言 - C、C++、Rust、Go 等语言现在可以将代码编译为 WebAssembly,并在 Web 浏览器中运行。因此,以前无法在浏览器中运行的语言现在可以实现了。

  • 更快、高效且可移植 - 由于代码体积小,因此加载和执行速度更快。

  • 易于理解 - 开发人员不必花费太多精力来理解 WebAssembly 编码,因为他们不必用 WebAssembly 编写代码。而是将代码编译为 WebAssembly,并在 Web 上执行。

  • 易于调试 - 尽管最终代码是低级汇编语言,但您也可以将其获取为易于阅读和调试的文本格式。

WebAssembly 的缺点

以下是 WebAssembly 的缺点 -

  • WebAssembly 仍在开发中,现在就断言其未来还为时过早。

  • WebAssembly 依赖于 javascript 与文档对象模型 (DOM) 进行交互。

WebAssembly - 简介

WebAssembly 也称为 WASM,于 2017 年首次推出。WebAssembly 起源背后的主要科技公司包括 Google、Apple、Microsoft、Mozilla 和 W3C。

坊间传闻称 WebAssembly 由于其更快的执行速度将取代 Javascript,但事实并非如此。WebAssembly 和 Javascript 旨在协同工作以解决复杂问题。

WebAssembly 的需求

到目前为止,只有 Javascript 可以在浏览器内部成功工作。浏览器中有一些非常繁重的任务难以使用 javascript 执行。

举几个例子,例如图像识别、计算机辅助设计 (CAD) 应用程序、实时视频增强、VR 和增强现实、音乐应用程序、科学可视化和模拟、游戏、图像/视频编辑等。

WebAssembly 是一种具有二进制指令的新语言,可以更快地加载和执行。上述任务可以使用 C、C++、Rust 等高级语言轻松完成。我们需要一种方法,将我们用 C、C++、Rust 编写的代码编译并用于 Web 浏览器。使用 WebAssembly 可以实现这一点。

当 WebAssembly 代码加载到浏览器内部时。然后,浏览器负责将其转换为处理器可以理解的机器格式。

对于 javascript,代码必须下载、解析并转换为机器格式。这需要花费大量时间,对于我们之前提到的繁重任务来说,速度可能会非常慢。

WebAssembly 的工作原理

像 C、C++ 和 Rust 这样的高级语言被编译成二进制格式,即 .wasm 和文本格式 .wat

Working of WebAssembly

用 C、C++ 和 Rust 编写的源代码使用编译器编译为 .wasm。您可以使用 Emscripten SDK 将 C/C++ 编译为 .wasm

流程如下 -

Wasm

可以使用 Emscripten SDK 将 C/C++ 代码编译为 .wasm。随后,可以在 html 文件中借助 javascript 使用 .wasm 代码来显示输出。

WebAssembly 的关键概念

关键概念解释如下 -

模块

模块是由浏览器编译为可执行机器代码的对象。模块被认为是无状态的,可以在窗口和 Web 工作线程之间共享。

内存

WebAssembly 中的内存是一个arraybuffer,用于保存数据。您可以使用 Javascript api WebAssembly.memory() 分配内存。

WebAssembly 中的表是一个类型化数组,即 WebAssembly 内存外部,并且主要包含对函数的引用。它存储函数的内存地址。

实例

实例是一个对象,它将包含所有可以从 javascript 调用的导出函数,以便在浏览器中执行。

WebAssembly - WASM

WebAssembly 也称为 wasm,是对 Javascript 的改进。它被设计为像 javascript 一样在浏览器中运行,也可以在 nodejs 中运行。当任何高级语言(如 C、C++、Rust)编译时,您会获得 wasm 输出。

考虑以下 C 程序 -

int factorial(int n) {
   if (n == 0) 
      return 1; 
   else 
      return n * factorial(n-1); 
}

使用 WasmExplorer(可在 https://mbebenita.github.io/WasmExplorer/ 获取)获取如下所示的编译代码 -

Wasm Explorer

阶乘程序的 WebAssembly 文本格式如下所示 -

(module 
   (table 0 anyfunc) 
   (memory $0 1) 
   (export "memory" (memory $0)) (export "factorial" (func $factorial)) 
   (func $factorial (; 0 ;) (param $0 i32) (result i32)
      (local $1 i32) 
      (local $2 i32) 
      (block $label$0 
         (br_if $label$0 
            (i32.eqz 
               (get_local $0) 
            )
         )
         (set_local $2 
            (i32.const 1) 
         ) 
         (loop $label$1 
            (set_local $2 
               (i32.mul 
                  (get_local $0) (get_local $2) 
               ) 
            ) 
            (set_local $0 
               (tee_local $1        (i32.add 
                  (get_local $0) (i32.const -1) 
               ) 
               ) 
            ) 
            (br_if $label$1      (get_local $1) 
            ) 
         ) 
         (return 
            (get_local $2)
         ) 
      ) 
      (i32.const 1) 
   )
)

使用 Wat2Wasm 工具,您可以查看 WASM 代码,就像下面提到的那样 -

Wat2Wasm Tool

开发人员不应该用 wasm 编写代码或学习用它编写代码,因为它主要是在编译高级语言时生成的。

栈式机器模型

在 WASM 中,所有指令都推送到栈上。参数会被弹出,结果会被推回栈上。

考虑以下 WebAssembly 文本格式,它将两个数字相加 -

(module
   (func $add (param $a i32) (param $b i32) (result i32) 
      get_local $a 
      get_local $b 
      i32.add
   )
   (export "add" (func $add))
)

函数的名称为 $add,它接收 2 个参数 $a 和 $b。结果为 32 位整数类型。局部变量使用 get_local 访问,加法运算使用 i32.add 执行。

执行期间将两个数字相加的栈表示如下 -

Stack

步骤 1中 - 执行 get_local $a 指令,第一个参数即 $a 被推送到栈上。

步骤 2中 - 在执行 get_local $b 指令期间,第二个参数即 $b 被推送到栈上。

步骤 3中 - i32.add 的执行将从栈中弹出元素,并将结果推回栈上。最后栈中保留的值是函数 $add 的结果。

WebAssembly - 安装

在本节中,我们将学习如何安装 Emscripten SDK 来编译 C/C++。Emscripten 是一种低级虚拟机 (LLVM),它接收从 C/C++ 生成的字节码并将其编译成可以轻松地在浏览器中执行的 JavaScript。

要将 C/C++ 编译为 WebAssembly,我们首先需要安装 Emscripten sdk。

安装 Emscripten sdk

安装 Emscripten sdk 的步骤如下 -

步骤 1 - 克隆 emsdk 仓库:git clone https://github.com/emscripten-core/emsdk.git

E:\wa>git clone https://github.com/emscripten-core/emsdk.git 
Cloning into 'emsdk'... 
remote: Enumerating objects: 14, done. 
remote: Counting objects: 100% (14/14), done. 
remote: Compressing objects: 100% (12/12), done. 
remote: Total 1823 (delta 4), reused 4 (delta 2), pack-reused 1809 receiving obje 
cts: 99% (1819/1823), 924.01 KiB | 257.00 KiB/s 
Receiving objects: 100% (1823/1823), 1.01 MiB | 257.00 KiB/s, done. 
Resolving deltas: 100% (1152/1152), done.

步骤 2 - 进入 emsdk 目录。

cd emsdk

步骤 3 - 对于 Windows:执行以下命令。

emsdk install latest

对于 Linux,此命令将花费一些时间来安装必要的工具,例如 java、python 等。请遵循以下代码 -

./emsdk install latest

步骤 4 - 要激活最新的 SDK,请在终端中执行以下命令。

对于 Windows,执行以下命令 -

emsdk activate latest

对于 Linux,执行以下命令 -

./emsdk activate latest

步骤 5 - 要激活 PATH 和其他环境变量,请在终端中运行以下命令。

对于 Windows,执行命令 -

emsdk_env.bat

对于 Linux,执行以下命令 -

source ./emsdk_env.sh

我们已经完成了 emsdk 的安装,现在可以编译 C 或 C++ 代码了。C/C++ 的编译将在后面的章节中进行。

要编译任何 C 或 C++ 代码,以下为命令 -

emcc source.c or source.cpp -s WASM=1 -o source.html

输出将为您提供 source.html 文件、source.js 和 source.wasm 文件。js 将包含用于获取 source.wasm 的 api,当您在浏览器中点击 source.html 时,您可以看到输出。

要仅获取 wasm 文件,可以使用以下命令。此命令将仅为您提供 source.wasm 文件。

emcc source.c or source.cpp -s STANDALONE_WASM

WebAssembly - 编译为 WASM 的工具

本章将讨论一些易于使用的工具,这些工具在使用 WebAssembly 时非常有用。让我们从学习 WebAssembly.studio 工具开始。

WebAssembly.studio

此工具允许您将 C、Rust、Wat 编译为 Wasm 等。

WebAssembly Studio

首先,您可以点击 Empty C Project、Empty Rust Project、Empty Wat Project 将 C 和 Rust 编译为 WASM。5。

Empty C Project

它具有 Build、Run 功能,用于构建代码并检查输出。下载按钮允许您下载.wasm文件,该文件可用于在浏览器中进行测试。此工具非常有助于编译 C 和 Rust 代码并检查输出。

WebAssembly Explorer

WebAssembly Explorer 允许您编译 C 和 C++ 代码。有关更多详细信息,请参阅链接 https://mbebenita.github.io/WasmExplorer/。点击链接后将显示如下屏幕 -

WebAssembly Explorer

您可以选择 C 和 C++ 版本。C 或 C++ 的源代码在此处编写 -

Compile Button

点击 Compile 按钮后,它会在下面的块中给出 WebAssembly 文本格式 (WAT) 和 Firefox x86 汇编代码 -

WAT

您可以下载.wasm代码并在浏览器中进行测试。

WASMFiddle

Wasmfiddle 帮助你将 C 代码编译成 WebAssembly,并测试输出结果。点击链接 https://wasmfiddle.com/ 后,你将看到以下页面:

WASM Fiddle

点击“构建”来编译代码。你可以点击“Wat”和“Wasm”来下载 Wat 和 Wasm 代码。要测试输出结果,请点击“运行”按钮。

WASM 到 WAT

工具 **wat2wasm** 会在你输入 WebAssembly 文本格式时生成 wasm 代码。你可以点击链接 https://github.webassembly.net.cn/wabt/demo/wat2wasm/ 查看演示,出现的屏幕如下所示:

WASM to WAT

你可以使用上传按钮上传 .wasm 文件,文本区域将显示文本格式。

WAT 到 WASM

工具 wat2wasm 会在你输入 WebAssembly 文本格式时生成 wasm 代码。你可以点击链接 https://github.webassembly.net.cn/wabt/demo/wat2wasm/ 查看演示,出现的屏幕如下所示:

WAT to WASM

这个工具非常有用,因为它可以帮助你获得并测试输出结果。你可以输入 WAT 代码,查看 .wasm 代码,并执行代码以查看输出。

WebAssembly - 程序结构

WebAssembly,也称为 WASM,是一种二进制格式的低级代码,旨在以最有效的方式在浏览器中执行。WebAssembly 代码由以下概念构成:

  • 类型
  • 指令

让我们详细了解一下。

WebAssembly 中的值用于存储复杂数据,例如文本、字符串和向量。WebAssembly 支持以下类型:

  • 字节
  • 整数
  • 浮点数
  • 名称

字节

字节是 WebAssembly 支持的最简单形式的值。值采用十六进制格式。

例如

表示为 *b* 的字节,也可以取自然数 n,其中 n < 256。

byte ::= 0x00| .... |0xFF

整数

在 WebAssembly 中,支持的整数如下所示:

  • i32:32 位整数
  • i64:64 位整数

浮点数

在 WebAssembly 中,支持的浮点数如下所示:

  • f32:32 位浮点数
  • f64:64 位浮点数

名称

名称是字符序列,其标量值由 Unicode 定义,可以在此处提供的链接 http://www.unicode.org/versions/Unicode12.1.0/ 中找到。

类型

WebAssembly 中的实体被分类为类型。支持的类型如下所示:

  • 值类型
  • 结果类型
  • 函数类型
  • 限制
  • 内存类型
  • 表类型
  • 全局类型
  • 外部类型

让我们逐一学习它们。

值类型

WebAssembly 支持的值类型如下所示:

  • i32:32 位整数
  • i64:64 位整数
  • f32:32 位浮点数
  • f64:64 位浮点数
valtype ::= i32|i64|f32|f64

结果类型

括号内编写的值会被执行并存储在结果类型中。结果类型是代码块(由值组成)执行的输出。

resulttype::=[valtype?]

函数类型

函数类型将接收参数向量并返回结果向量。

functype::=[vec(valtype)]--> [vec(valtype)]

限制

限制是与内存和表类型关联的存储范围。

limits ::= {min u32, max u32}

内存类型

内存类型处理线性内存和大小范围。

memtype ::= limits

表类型

表类型根据分配给它的元素类型进行分类。

tabletype ::= limits elemtype
elemtype ::= funcref

表类型取决于分配给它的最小和最大大小的限制。

全局类型

全局类型保存具有可以更改或保持不变的值的全局变量。

globaltype ::= mut valtype
mut ::= const|var

外部类型

外部类型处理导入和外部值。

externtype ::= func functype | table tabletype | mem memtype | global globaltype

指令

WebAssembly 代码是一系列遵循堆栈机器模型的指令。由于 WebAssembly 遵循堆栈机器模型,因此指令会被压入堆栈。

例如,函数的参数值会从堆栈中弹出,结果会被压回堆栈。最终,堆栈中只有一个值,即结果。

一些常用的指令如下所示:

  • 数值指令
  • 变量指令

数值指令

数值指令是对数值执行的操作。

例如
nn, mm ::= 32|64
ibinop ::= add|sub|mul|div_sx|rem_sx|and|or|xor
irelop ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx
frelop ::= eq | ne | lt | gt | le | ge

变量指令

变量指令与访问局部变量和全局变量有关。

例如

访问局部变量:

get_local $a
get_local $b

设置局部变量:

set_local $a
set_local $b

访问全局变量:

get_global $a
get_global $b

设置全局变量:

set_global $a
set_global $b

WebAssembly - JavaScript

本章将列出 WebAssembly 和 Javascript 之间的比较。

Javascript 是一种我们在浏览器中大量使用的语言。现在,随着 WebAssembly 的发布,我们也可以在浏览器中使用 WebAssembly。

WebAssembly 的出现并非为了取代 javascript,而是为了处理 javascript 难以处理的某些事情。

例如

使用 javascript 难以完成图像识别、CAD 应用、实时视频增强、VR 和增强现实、音乐应用、科学可视化和模拟、游戏、图像/视频编辑等任务。

使用现在可以编译成 WebAssembly 的高级语言(如 C/C++、Rust),可以轻松完成上述任务。WebAssembly 生成易于在浏览器中执行的二进制代码。

因此,以下是 Javascript 和 WebAssembly 之间的比较列表。

参数 Javascript WebAssembly

编码

你可以轻松地用 Javascript 编写代码。编写的代码是人类可读的,并保存为 .js 文件。在浏览器中使用时,你需要使用 `