回顾 2021
过去一年 WebAssembly 的发展远比我们之前预测的要快很多,Safari web 浏览器这方面尤为突出。
Safari 浏览器
在 2017 年 WebAssembly 刚刚发布 MVP 版本的时候,Safari 还和其他浏览器一样处在相同水平线上。然后经过多年的发展,Safari 很不幸被甩在了后面。
时间来到 2021 年,看到 Safari 不断发布对 WebAssembly 支持的更新,我感到兴奋不已。这或许是 Safari 赶超其他厂商的开始。随着 2021 年 12 月 14 日 Safari 15.2 版本的发布,2021 年全年 Safari 更新发布了如下功能特性:
- 流编译
- 大内存操作
- 可寻址内存达到 4GB
- 异常处理
- 支持 COOP 和 COEP 的响应头
- 原子指令
- 对于使用了 COOP/COEP 响应头的网站重新开启对共享缓存区的支持
共享缓存区
借助共享缓存区,WebAssembly 可以在多线程之间实现内存共享。不过可惜的是,由于共享缓存区目前存在 Spectre 和 Meltdown 安全漏洞,因此在人们找到合适的漏洞解决方案之前,共享缓存区是被禁用的。
Chrome 桌面应用是第一个通过采用站点隔离作为漏洞解决方案来重新启用共享缓存区的。至此之后,在响应头添加 COOP 或 COEP,就意味着是告诉浏览器需要创建一个隔离的环境,以便安全地重新启用共享缓存区。
Firefox 桌面应用则是在 2020 年首次通过在响应头中添加这些响应头来重新启用共享缓存区。到了 2021 年初的时候,Chrome 桌面应用将对共享缓存区的支持更新到了最新标准。此后,如果你想使用共享缓存区功能,就必须添加这些响应头了。
同时,Chrome 的安卓端也在 2021 年初宣布对这些响应头的支持,使得在移动端使用 WebAssembly 的多线程成为可能。随着 Safari 最新版本重新开启对共享缓存区的支持后,除了 Firefox 移动端之外,所有的现代浏览器都支持了 WebAssembly 的多线程。
我本来预计 Firefox 移动端会在 2021 年支持这些响应头,不过可惜并没有发生。不过在 2022 年 Firefox 移动端极有可能完成对这些响应头的支持。
固定宽度 SIMD
SIMD 是将相同的指令同时作用在数据的多个节点上,通过这种方式可以大幅提高计算性能。例如,通过利用 CPU 的 SIMD 指令的优势,可以大幅提高图片处理能力。
SIMD 有很多种类型,作为一个开始,WebAssembly 决定先支持 128 位固定宽度 SIMD 操作。Chrome 和 Firefox 分别在 2021 年 5 月和 6 月增加了对固定宽度 SIMD 的支持,不过目前 Safari 还未支持。
异常处理
直到现在,WebAssembly 还没有内置的异常处理模块,为了弥补这个空缺,应用不得不使用 JavaScript 来增加额外的异常处理代码。然而,当将这些异常处理的逻辑代码构建到模块内部后,不但会使模块体积变大而且还会影响性能。所以,通常建议非必需就不要在模块中使用异常处理。
Chrome 在 9 月份正式发布了异常处理,不过,令我惊讶的是,Safari 也在 12 月份实现了异常处理功能并且决定在 15.2 版本中正式对外发布。
目前 Firefox 和 Node.js 还不支持,他们还在努力开发中。
根据 V8 (Chrome 和 Node.js 的 JavaScript 引擎)的发布说明显示,使用 WebAssembly 的异常处理比使用 JavaScript 的异常处理代码大小下降了 43%,与不使用任何异常处理相比代码大小增加了 9%。与此同时,WebAssembly 的异常处理对性能无影响,而 JavaScript 的异常处理则会对性能产生 30% 的负面影响。
如果对 V8 版本中针对 WebAssembly 异常处理的详细信息感兴趣的话,可以查看 V8 版本发布说明。
模块链接和接口类型
模块链接提案是关于在两个或多个模块定义之间建立链接,且让 WebAssembly 运行时在运行期间为你处理这种链接的过程。
接口类型提案描述的是模块相互之间如何通过高级的数据类型定义实现相互之间的通信。例如,一个模块可能使用 UTF-8 字符串,而另一个模块可能使用 UTF-16 字符串,通过描述它们的数据类型,WebAssembly 运行时就会更加容易实现模块间的通信。
我原先预计这两项提案会在 2021 年完成,目前看来,虽然取得了一些阶段性的成果,但未来仍然需要继续发力。
.NET 6
过于的一年里,为了进一步提高对 WebAssembly 的支持,不管是在工具方面还是性能方面,.NET 都做了很多努力。在 11 月份 6 版本中发布的 AOT 编译功能就是其中之一。
通常.NET 代码的编译分两步,首先将本地代码编译为 IL(.NET 架构中的中间语言),然后在部署的目标机器上,通过目标机器的即时编译完成剩下的编译。这个过程其实和 WebAssembly 的工作机制有点类似。
当这种编译机制的代码运行在客户端浏览器的时候,WebAssembly 代码就是.NET WebAssembly 运行时本身,而应用的代码则全是 IL 文件。这种按需编译执行 IL 文件的方式在性能上无法和直接执行已编译文件的方式相提并论。
在 AOT 编译方式中,应用的.NET 代码将全部被编译为 WebAssembly。这种方式虽然增加了文件的大小,但却获得了更好的性能,不过,大文件会导致应用的首次加载时间过长。
考虑到 IL 和 AOT 两种不同编译方式的优劣,使用配置导向的 AOT 编译或许是最佳选择。通过这种方式,我们可以将频繁使用的代码通过 AOT 进行编译,而剩下的部分则采用 IL 的编译方式。
除此之外,我们的 Uno 平台还引入了一个名为 XAML 资源修剪的功能。它可以与 AOT 编译一起删除那些未使用的代码。人们在测试中发现,通过这种方式可以使 WebAssembly 应用程序的代码减少 50%。
字节代码联盟
字节码联盟最初是由英特尔、Mozilla、红帽和 Fastly 组成的一个组织,其目标是通过利用 WebAssembly 和 WASI 等标准,构建一个可以在任何平台、设备或操作系统上运行不可信代码的安全平台。
WASI(WebAssembly 系统接口)是如何在浏览器之外的场景安全且一致的使用 WebAssembly 的标准。如果你想了解更多相关信息,Lin Clark 写了一篇很好的文章来解释 WASI: WASI-WebAssembly 系统接口标准。
联盟从一开始就希望更多的组织加入,为此组织内部的架构必须做相对应的调整,才可以满足组织未来的发展和壮大。在创建基金会的时候,红帽公司退出,微软加入且帮助其他创始人将该组织合并为一个非营利性组织。2021 年,联盟得以扩大,现在组织成员包括了微软、谷歌、Arm、DFINITY 基金会、普莱普工作室、Shopify 和加州大学圣地亚哥分校。
除了支持 WASI,该联盟也是 Wasmtime、cranlift、Lucet、WAMR 和 Enarx 等项目的来源。如果你对字节码联盟感兴趣,可以通过链接获取更多信息。
WebAssembly 应用领域
每年我们都看到越来越多的商业产品增加了对 WebAssembly 的支持。例如,在 2020 年,Zoom、谷歌 Meet、谷歌 Earth 和 Firefox 浏览器都加入了诸如 Cloudflare Workers 等基于 WebAssembly 的无服务计算产品竞的争行列。除此之外,还有 eBay 的条形码扫描仪、AutoCAD 的 web 应用程序以及 Unity 游戏引擎。
2021 年也不例外,下面是一些运用 WebAssembly 的新领域:
- Disney+ 应用程序开发工具包使用 WebAssembly
- 网页上发布了一个简化版的 Photoshop
- 微软的飞行模拟器有一个基于 webassembly 的插件系统
随着功能和工具的改进,以及越来越多的商业产品使用 WebAssembly,我们开始看到 WebAssembly 在框架和常规 Web 上的应用。虽然应用的领域和产品还是很少,但是他的持续增长是令人兴奋的。
2021 年对 WebAssembly 来说是伟大的一年,那么我们对 2022 年有什么期待呢?
预测 2022
我认为 2022 年一定会发生的事情是,WebAssembly 在几乎所有领域的功能会得到进一步的增强和改善。这其中我最期待的是异常处理。
异常处理
异常处理是许多编程语言必要的一个主要特性,因此它位于待办事项列表的前列。Safari、Chrome 和 Edge 已经具备了此功能,并且 Firefox 和 Node.js 也在积极开发中。
由于低性能的 JavaScript 版本依然还能继续使用,因此当你的模块中需要用到异常处理的时候,如果可以使用性能更好的 WebAssembly 异常处理那么就升级使用,否则就回退到使用 JavaScript 异常处理的版本。
共享缓存区
正如前面提到的,包括 Firefox 桌面在内,几乎所有现代的桌面端和移动浏览器现在都支持了 COOP/COEP 响应头。这些响应头允许浏览器安全地启用共享缓存区,从而允许你的模块使用 WebAssembly 多线程。
在现代浏览器中,现在只剩下 Firefox 移动端不支持这些响应头,不过 Firefox 移动端已经规划在 2022 年 2 月发布的 97 版本中支持这些响应头。
固定宽度 SIMD
我预测此功能将在今年的 Safari 版本中实现。我的理由是,Safari 在 2021 年增加了对 WebAssembly 的支持,WebAssembly 固定宽度 SIMD 规范现在已经标准化,而且 Xcode(苹果的操作系统开发环境) 也已经支持了 SIMD。
尾部调用
为了支持 WebAssembly,一些编程语言不得不运用尾部调用,虽然很多事情都可以有变通的路径,但其过程缓慢。除此之外,尾部调用在编译优化和流程控制上也是有积极的作用。该提案已经完成一段时间了,但至如果想要进入到第四阶段,就必须至少有 2 个厂商(Chrome、Firefox 或 Safari)实现此功能。Chrome 已经在一个版本标签中实现了此功能,但在它达到第四阶段之前 Chrome 并不打算正式发布它。所以我们仍然必须等待至少再多一个厂商实现此功能。
并非各厂商不想实现此功能,而是他们都为各自认为更重要的事情而忙碌。因此人们正试图提高此功能在各个厂商眼中的重要性和优先级。
支持多内存
多内存的一个使用场景是,模块将一个内存区域作为自己的内部数据区域使用,将另外一个内存区域传递给某些需要写入数据的模块使用。通过此方式,你可以防止模块内部数据因为外部模块的异常写入而发生错误。同时这种方式还可以很好的隔离敏感数据和共享数据,起到一定的安全作用。
另一个多内存的使用场景是,在 WebAssembly 的多线程中,你可以让这些线程有一个共享的内存区域,同时将其他的模块数据保存到另外一个内存区域中。
如果你对多内存区域的使用场景感兴趣,可以查看多内存介绍来了解更多使用场景。
WASI(WebAssembly 系统接口)
在本文的前面提到,我预期模块链接和接口类型两个提案会在 2021 年完成。不过可惜,它们目前依然还在推进中,并没有像我的预期那样在 2021 年完成。
这些建议不仅仅是 WebAssembly 的一部分,它们还是创建组件模型必不可少的功能。根据此 WASI 的帮助文档描述,组件模型和操作系统的进程模型类似,都是用来定义进程是如何启动以及相互之间通信的。WASI 的在这里扮演的角色就类似操作系统的 API 层。
所以,2022 年组件模型和 WASI 的接口类型提案都将持续发展。如果你对 WASI 的提案感兴趣,可以通过 WASI 提案列表进行查看。
总结
过去的一年里,在提高 WebAssembly 性能方面,我们看到了 WebAssembly 多线程的共享缓冲区、固定宽度 SIMD 和异常处理等特性。同时,.NET6 中提高了对 WebAssembly 的支持,并且.NET 和 Uno 平台都通过增加 AOT,进一步提高 WebAssembly 的性能。
Safari 在 2021 年是一个大惊喜,他们在追赶其他浏览器的 WebAssembly 支持上做了很多工作。
在 2021 年,我们看到更多的商业产品加入了使用 WebAssembly 的行列,同时,WebAssembly 也开始在大众网络上得到应用。如此看来,2022 年必将会是 WebAssembly 发展的又一个好年份,因为那些现在还不支持的功能特性,很有可能在 2022 最终正式发布且投入生产。
针对 WebAssembly 本身有非常多有趣的功能提案,同时字节码联盟也将继续助力 WebAssembly 在浏览器以为场景的落地和功能升级。如果你对 WebAssembly 功能支持的发展路径有兴趣的话,可以通过下面的网站进行了解。