10月21日,杭州云栖大会-技术&可持续发展论坛,注册有礼 了解详情
写点什么

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

2018 年 8 月 05 日

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 年 8 月 05 日 09:501269
用户头像

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

关注

评论

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

写代码的这群人不写文档,为什么 (上篇)

御剑

程序员 架构

翻译:《实用的Python编程》07_03_Returning_functions

codists

Python

一文了解数据库资源管理技术

华为云开发者社区

数据库 存储 GaussDB(DWS) 资源管理

宣传

Ashley.

微信聊天记录里的文件又失效了?试试这个文件同步开源项目吧

HelloGitHub

Go 开源 文件传输

跟随报文,开启一段奇妙之旅

华为云开发者社区

报文 Windows主机 路由器 Linux主机 路由表

ThreadLocal 慌不慌?

叫练

JVM ThreadLocal 引用 软引用

翻译:《实用的Python编程》07_02_Anonymous_function

codists

Python

Flutter开发:给image设置圆角的方法

三掌柜

flutter 3月日更

基于NA公链(Nirvana)的应用和NAC公链社区共识探究公链的发展未来

区块链第一资讯

分布式任务 + 消息队列框架 go-queue

万俊峰Kevin

Go 微服务 分布式任务 消息队列

彩色图像的二值化,取经之旅第 6 天

梦想橡皮擦

28天写作 3月日更

技术实践丨Prometheus+Grafana搭建HBase监控仪表盘

华为云开发者社区

开源 Grafana Prometheus HBase 开源数据库

寻找被遗忘的勇气(二十一)

Changing Lin

3月日更

与前端训练营的日子 -- Week20

SamGo

学习

JVM - GC 问题分析常用两大指标

insight

3月日更

在深圳,看见大鲸跃起的浪潮

浪潮云

工业互联网

LiteOS:剖析时间管理模块源代码

华为云开发者社区

时间管理 时间 LiteOS huawei 任务

ES6中的新特性:Iterables和iterators

程序那些事

nodejs ES6 程序那些事

MongoDB中的正则表达式

Kylin

mongodb 3月日更 21天挑战

前端开发:自定义搜索框(含联想搜索)

三掌柜

vue.js 前端 3月日更

Flink SQL 在网易云音乐的产品化实践

Apache Flink

flink

字节跳动单点恢复功能及 Regional CheckPoint 优化实践

Apache Flink

flink

Ubuntu 日常使用问题及解决

依旧廖凯

28天挑战 3月日更

字符编码,原来是SQL不走索引的元凶之一!

Java小咖秀

MySQL 程序员 开发 bug 细节

vivo 应用商店推荐系统探索与实践

vivo互联网技术

架构 推荐系统 服务器

uni-app跨端开发H5、小程序、IOS、Android(六):uni-app事件绑定

黑马腾云

微信小程序 uni-app 前端 iOS Developer 3月日更

基于数组或链表实现Map

Silently9527

数据结构和算法

Swagger增强神器:Knife4j!用它轻松实现接口搜索、Word下载、接口过滤...

王磊

Java swagger Knife4j

【LeetCode】位1的个数Java题解

HQ数字卡

算法 LeetCode 3月日更

书单|互联网企业面试案头书之架构师篇

博文视点Broadview

架构

数据cool谈(第1期)数据库寻路,开源有态度

数据cool谈(第1期)数据库寻路,开源有态度

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