AICon 上海站|日程100%上线,解锁Al未来! 了解详情
写点什么

WebAssembly 的未来:潜在新特性一览

  • 2018-08-05
  • 本文字数:4119 字

    阅读完需:约 14 分钟

WebAssembly 简介

WebAssembly 开发团队的描述:

WebAssembly(或 wasm)是一种适用于 Web 的可移植编译格式,提供更小的文件尺寸和更快的加载速度。

实际上,WebAssembly 旨在成为高级语言的编译目标。目前可以使用 C、C++、Rust、Go、Java、C#编译器(还有更多)来创建 wasm 模块。

WebAssembly 模块以二进制的格式发送到浏览器,并在专有虚拟机上执行。这个虚拟机与 JavaScript 虚拟机共享资源,如内存和线程。WebAssembly 模块总是与 JavaScript 代码一起使用,在必要的时候可以执行一些有用的操作。

目前正在进行中的很多提案旨在让 WebAssembly 成为更好的编译目标,并减少 JavaScript“胶水”代码。

WebAssembly 提案

WebAssembly 提案的流程是分阶段的,从阶段 1(不成熟)到阶段 5(完成标准化)。以下是目前所有提案的清单(前 4 个阶段),每个提案都将在后面详细介绍。

第 1 阶段(功能提案)包括以文本格式自定义注解、主机绑定、尾调用、批量内存操作、ECMA 模块集成、垃圾回收、异常处理、固定宽度 SIMD、线程。第 2 阶段(规范提议)包括 BigInt 转换。第 2 阶段(实现)包括引用类型和返回多个值。第 4 阶段(标准化)包括导出和导入可变全局变量和有符号扩展操作。

引用类型

目前的 WebAssembly 类型系统还很小,只有四种数字类型。目前,如果要使用复杂类型(例如字符串、对象、数组、结构体),需要将它们序列化为线性内存,并提供它们所在位置的引用。这个提案对类型系统进行了扩展,添加了一个新的 anyref 类型,模块可以持有对主机环境对象的引用,也就是说,你可以将 JS 对象传给 wasm 模块。

通过 anyref 引用的对象对于 wasm 模块来说意义不是很大,关键在于模块可以持有在 JS 堆上分配的对象的引用,这意味着在 wasm 执行期间需要对这些引用进行跟踪。该提案被视为垃圾回收提案的垫脚石。

返回多个值

WebAssembly 的虚拟机是基于栈的,操作对象在函数被调用之前是放在栈上的,函数会使用这些对象并替换为返回值。在目前的规范中,函数只能返回单个值。新的提案允许指令、函数和块返回多个值(例如,整数除法可以返回除数和余数)。

下面是一个简单的“swap”函数,它可以返回多个值(result i32 i32)。

复制代码
(func $swap (param i32 i32) (result i32 i32)
(get_local 1) (get_local 0)
)

导出和导入可变全局变量

目前,wasm 支持全局变量和局部变量。全局变量可以被导入和导出,并与 JS 宿主共享,不过必须将它们定义为不可变的。

新的提案将 WebAssembly.Global 构造函数添加到 JS API 中,允许导出和导入可变的全局变量。该特性对于跨多个 wasm 模块共享状态来说非常有用。

有符号扩展操作

符号扩展是一种在保留符号的同时增加二进制数位数的方法。这个提案添加了少量符号扩展指令,例如 i32.extend8_s——将有符号的 8 位整数扩展为 32 位整数。

FireFox 已经实现了这个特性,并计划在 9 月发布。

BigInt 转换

JavaScript 只有一个数字类型,即 IEEE 754 浮点数——这种表示法存在一定的局限性。现在有一个新兴的标准,即增加对“大整数”的支持,目前处于 TC39 流程的第 3 阶段。最终确定后,将为开发人员提供任意精度的整数。

WebAssembly 的四种数字类型之一是 64 位整数。新提案将提供完全的互操作,让 JS 的 BigInt 和 wasm 的 64 位整数实现双向转换。

顺便提一下,现在已经有一个用于在 JavaScript 中执行 64 位运算的库,叫作 long.js。最近,他们移除了基于 JavaScript 的实现,改用更简单的 WebAssembly!

线程

JavaScript 已经通过 WebWorker 实现了多线程,但是在 worker 之间只能使用 postMessage 进行较慢的消息传递。共享内存提案已经在 TC39 中定稿,并在 2017 年 2 月成为 ECMAScript 的一部分。共享数组缓冲区和原子性让线程之间共享数据变得更加容易。

WebAssembly 的这个提案也是允许共享访问线性内存,并提供原子操作。但值得注意的是,提案并没有引入创建线程的机制(引起了很多争议),而是由宿主提供此功能,也就是我们熟悉的 WebWorker。

我相信会有一些人对这个提案感到失望。不过,WebAssembly 之所以能够快速发展到 MVP 版本,跟团队专注于简单性不无关系。利用成熟的宿主功能(WebWorker)是非常有意义的。

将来可能会添加原生线程,但会作为单独的提案,不过这可能还需要几年的时间!

固定宽度 SIMD

单指令多数据流(SIMD)是一组支持矢量风格处理的指令,例如,将一个向量添加到另一个向量种。所有现代 CPU 都支持这些指令。有一个 SIMD.js TC39 提案,增加了很多 128 位类型,例如 float32 x 4,以及相应的操作(如 add、multiply),但最近删除了这部分内容,因为 WebAssembly 中已经添加了类似的功能。这样做是有道理的,因为这些是低级指令,而 WebAssembly 恰好是低级运行时。

WebAssembly 的 SIMD 提案非常简单,为 wasm 添加一个新的 128 位类型,可以表示四个数字的向量,以及用于创建和操作这些新类型的简单指令集。这将为某些算法类别的性能带来改进。

异常处理

程序会在出现异常时中断控制流,异常会顺着调用栈向上传播,直到遇到合适的“catch”块。异常处理是大多数现代编程语言的共同特征,尽管 Swift 在早期版本中并不支持它们。

WebAssembly MVP 在当前控制流指令中没有任何类似于异常处理的东西。因此不得不使用 Emscripten 这样的工具来模拟这个功能,但这是以牺牲性能为代价——目前,默认情况下捕获 C++ 异常是关闭的,其他语言也面临类似的问题。

异常处理提案概述了构建一个与宿主环境“良好配合”的概念所涉及的大量复杂性。有趣的是,这是第二次尝试创建这个提案,可见他们目前面临的挑战有多严峻!

该提案不仅要向 WebAssembly 添加异常处理,更是要引入一种更通用的事件概念,看起来很像中断。当事件发生时,执行被暂停,在 events 处安插一个恰当的处理程序。

除了事件,可能还会添加标准的 try/catch 指令:

复制代码
try block_type
instruction*
catch
instruction*
end

这将减少 WebAssembly 编译器的一些复杂性。

垃圾回收

大多数现代编程语言(不包括系统级语言)使用垃圾回收器进行内存管理。简而言之,垃圾回收器(GC)让开发人员无需过多考虑内存管理,他们可以创建对象、传递对象、在函数 / 变量之间共享对象,并且在不再使用这些对象时依靠 GC 来清理它们。

WebAssembly 没有垃圾回收器。事实上,它没有任何可用于内存管理的工具,它只是为你提供了一块“内存”。不使用 GC 的编程语言仍然需要某种机制来管理内存分配,例如 Rust 使用了一个小型的 WebAssembly 优化分配器。

目前,需要垃圾回收器的编程语言没有其他选择,只能将 GC 编译为 wasm,并将其作为二进制文件的一部分,例如 AssemblyScript 就在二进制文件中包含了一个“makeshift GC”。但这样会增加二进制文件的大小,同时 GC 算法的效率也会受到影响。缺少 GC 是 Scala 和 Elm 等语言还不支持编译成 WebAssembly 的原因。

这个提案将 GC 功能带到 WebAssembly 中。有趣的是,它不会有自己的 GC,而是与宿主环境的 GC 集成。还有其它各种其他提案(宿主绑定、引用类型)旨在改进与宿主的互操作性,从而更容易共享状态和调用 API。使用单个 GC 来管理内存会让这些变得更容易。

这个提案是一个重大变更,wasm 的类型系统因此添加了很多新的东西,包括简单的元组、结构体和数组。还有一些讨论是关于添加字符串类型的。

在 WebAssembly 中使用 GC 是可选的,这样 Rust/C++ 就可以使用内存分配器和线性内存。新类型将在新的 WebAssembly 堆上进行分配,尽管提案中未明确说明。我猜宿主堆也可以使用,但可能会带来很大的开销。

这个提案增加了很多新的指令,这是结构体的一个例子:

复制代码
;; structures with fields
(type $point (struct (field $x f64) (field $y f64) (field $z f64)))
 
;; allocated with new
(call $g (new $point (i32.const 1) (i32.const 2) (i32.const 3)))
 
;; field accessors - type checked when validated
(load_field $point $x (get_local $p)

ECMA 模块集成

ECMAScript 模块(ESM)是一个相对较新的规范,所有主流浏览器现在都已支持。

目前,wasm 模块是通过 HTTP 进行加载的,然后使用 JS API 进行实例化:

复制代码
const req = fetch("./myModule.wasm");
 
const instance = await WebAssembly.instantiateStreaming(req)
instance.exports.foo()

这个提案引入了一种机制,可以通过 ESM 导入的方式来加载 wasm 模块:

复制代码
import {foo} from "./myModule.wasm";
foo()

这让实例化 wasm 模块变得更简单,而更大的好处是它们成为 JS 模块图的一部分,也可以进行摇树优化(tree shaking)、捆绑、代码分割和 ESM 支持的其他优化。

批量内存操作

该提案增加了复制 / 填充线性内存区域的新操作。它们将为某些场景带来更好的性能。

尾调用

递归函数调用可能会导致很深的调用栈,会带来各种问题(性能、内存消耗、堆栈溢出的可能性)。通过尾调用优化,递归函数调用将被替换为迭代。这种技术对于函数式语言来说非常重要。为此,该提案引入了新的 return_call 指令。

宿主绑定

基于多种因素(wasm 类型系统太过简单、缺少引用类型等等),WebAssembly 与 JavaScript 宿主之间的当前接口非常有限。如果你想要编写一个操作 DOM 或使用其他浏览器 API 的 wasm 模块,必须编写大量“胶水”代码。

这个提案允许 WebAssembly 模块创建、传递、调用和操作 JavaScript/DOM 对象。它添加了一部分与宿主绑定相关的内容,其中包括用于描述绑定机制或接口的注解。

Rust 已经有了一个工具,叫作 wasm-bindgen,它的作用与该提案很相似。使用 wasm-bindgen,你就可以轻松地跨越 wasm 和 JS 传递字符串等对象。该工具将绑定元数据添加到 wasm 模块中,并生成所需的 JS 胶水代码。

以文本格式自定义注解

wasm 二进制格式支持将元数据添加到模块中。这个提案为文本格式也添加了类似的功能,这对宿主绑定来说非常有用。举个例子:

复制代码
(module
(func (export "f") (param i32 (@js unsigned)) ...)
)

@js unsigned 注解添加了用于生成宿主绑定的其他元数据。

结论

希望这些能让你对 WebAssembly 未来的发展方向有所了解。这些提案中的一些小改进可能很快就能完成,但大的改进可能需要几年时间才能完全实现。

查看英文原文: https://blog.scottlogic.com/2018/07/20/wasm-future.html

感谢覃云对本文的审校。

2018-08-05 09:502517
用户头像

发布了 731 篇内容, 共 462.7 次阅读, 收获喜欢 2005 次。

关注

评论

发布
暂无评论
发现更多内容

软件测试/测试开发丨持续集成体系介绍 学习笔记

测试人

软件测试 持续集成 CI/CD 测试开发

专家观点∣数字化场景应用助力铁合金企业增产提效

用友BIP

冶金 提质增效

基于深度学习的工业缺陷检测详解——从0到1

月球背面

人工智能 缺陷检测 工业视觉 #技术人的2023总结

一文详解kube-apiserver认证鉴权能力

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟

GitOps实践指南:GitOps能为我们带来什么?

阿里云大数据AI技术

软件测试/测试开发丨接口自动化测试-TCP与UDP的区别

测试人

软件测试 测试开发

Mysql tls 会话:再一次抓包之后,我认识到…

华为云开发者联盟

后端 开发 华为云 华为云开发者联盟

密切关注2024年及以后的加密货币业务趋势

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

即时通讯技术文集(第29期):IM开发技术合集(Part2) [共18篇]

JackJiang

网络编程 即时通讯 IM

库克透露苹果已有接班人计划,或从这四人中诞生;谷歌创始人亲自给 Gemini 写代码丨 RTE 开发者日报 Vol.114

声网

从 WasmEdge 运行环境读写 Rust Wasm 应用的时序数据

Greptime 格睿科技

数据库 rust Wasm

全面了解 Stable Diffusion API 调用教程

Liam

AI 前端 后端 API Stable Diffusion

如何判断Linux服务器是否被入侵了,该怎么对服务器进行安全加固

德迅云安全杨德俊

Linux 服务器安全 入侵检测

集成学习方法——随机森林

小齐写代码

云图说|有了这2招必杀技,你的主机“身陷重围”都不怕!

华为云开发者联盟

云计算 华为云 主机安全 华为云开发者联盟 华为云HSS

WebAssembly的未来:潜在新特性一览_JavaScript_Colin Eberhardt_InfoQ精选文章