共计 4523 个字符,预计需要花费 12 分钟才能阅读完成。
特别说明
这是一个由 simviso 团队对 2019 Google I/O 大会中关于面向 Web 开发人员的 WebAssembly 相关话题进行翻译的文档,内容并非直译,其中有一些是译者自身的思考。Surma 是 Google 公司 WEB 基础的贡献者,也是 open web 平台的开发倡导者。
上一篇博文地址:面向 Web 开发人员的 WebAssembly 2019 Google I/O 上
视频地址:面向 Web 开发人员的 WebAssembly 2019 Google I/O 上
视频地址:面向 Web 开发人员的 WebAssembly 2019 Google I/O 下
视频同时也获得了谷歌大佬 Surma 和 Deepti 的官方推特分享和点赞
视频翻译文字版权归 simviso 所有
前言
现在我们来谈谈 WebAssembly 的未来和即将到来的未来。欢迎 Deepti 上台发言。
谢谢,Surma。
Hi,各位,我叫 Deepti。我是 Chrome 团队的软件工程师,我致力于标准化 WebAssembly 功能,并在 V8 中实现它们。
到目前为止,你在本演讲中所看到的大部分内容都已经在所有主要浏览器中落地实现了。也可以说这是 MVP 或者说是 WebAssembly 的最小可行性产品。(译者注:MVP(Minimum Viable Product –最简化可实性产品)是一种在创业团队中非常流行和实用的产品理念,旨在通过提供最小化可行产品获取用户反馈,并在这个最小化可行产品上持续快速迭代,直到产品到达一个相对稳定的阶段。)
并且,我们一直在努力增加功能,以确保我们越来越接近本地性能。这个 MVP 本身就是打破了 Web 平台的限制,可以在它之上运行的一整套新的应用程序。但这不是最终目标,社区组织和实现者正在努力实现很多令人兴奋的新功能。
WebAssembly 多线程提案
其中第一个就是 WebAssembly 多线程提案。多线程提案引入了并行计算。
具体的说,这意味着它引入了线程之间共享线性内存的概念,以及原子指令的语义。现在,为什么有必要这么做?
有许多用 C 或 C ++ 编写的现有库使用了 Pthreads(POSIX 线程(POSIX threads)),它们可以编译成 wasm,并且以多线程模式运行,允许不同线程并行处理相同的数据。除了启用新功能外,对于受益于多线程执行的应用程序,你会看到性能随着线程数的增加而增长。因此,这个线程提案是建立在 Web 平台中已经支持多线程的基础之上。
Web 已经支持使用 Web Workers 来进行多线程执行,这刚好可以用来作为在 WebAssembly 中引入多线程执行的基础。
Web Workers 的缺点是他们之间不能共享可变数据。反而,他们依赖消息传递,通过发送消息进行通信。因此它们通过消息传递进行通信。所以每一个 WebAssembly 线程都在一个 Web Worker 中运行。
但是它们共享了 WebAssembly 内存允许他们处理相同的数据。使它们的运行速度接近于本地速度。
这里的共享线性内存建立在 JS 共享数组 Buffer 上。
因此,如果你看这幅图,发现它们每个线程都运行在一个 Web Worker 中,并且使用一样的线性存储实例化一个 WebAssembly 实例。
这意味着这些线程实例的操作可以在共享内存上进行,但各自都有它们自己的独立执行堆栈。因此,用于创建 WebAssembly 内存的 API 几乎保持不变。
如果你看到图中第一行代码,你创建一个包含 shared 和 maximum 参数的 WebAssembly 内存。这将在下面创建一个共享数组 Buffer,其中 initial(初始)大小是由我们指定的,即一页内存。
因此,这些线程都在同一个内存上操作,我们怎么确保数据是一致的?
原子修改允许我们执行某种级别的同步。所以,当一个线程执行原子操作时,其他线程在它发生的瞬间会立即看到。但实际上完全同步的操作往往会阻塞一个线程,直到其他线程完成执行。
所以这个提案有一个互斥实现的例子,并且我介绍了如何通过 JavaScript 使用它。
如果你仔细观察,你会发现你在工作中所做的与在主线程上所做的之间所存在微妙的差异。
因此,在主线程上调用 tryLockMutex 方法,该方法尝试在给定 address 上添加一把互斥锁。如果互斥锁锁定成功,则返回 1,否则返回 0。在工作线程上,它会在给定地址进行互斥锁的锁定,重试直到锁定成功。所以基本在 Web 上就是通过这样一种方式来构建的(线程模型)。
实际上你不能阻塞主线程。这是我们在使用线程过程中需要记住的,这很有用。那么该提案 现在处于一个什么状态?该提案一直在稳定推进中。
当下正在进行的工作是确定共享数据 Buffer 使用的内存模型。
真正让我兴奋的是它已经在 Chrome 74 中进行提供,默认情况下是启用状态。Surma 在之前的演讲中提到了 QT,并且 QT 有完整的线程支持,因此你可以在你的应用程序中基于多线程使用它。对于此处来说,这里的共享内存使用的是 JavaScript 下的共享数组 Buffer。但在某些浏览器上是临时禁用的。
默认情况下,所有浏览器上的 WebAssembly 多线程目前都无法使用。但你仍然可以在 Firefox Nightly 版上试试这个。
WebAssembly 的目标之一是对现代硬件进行低级抽象。对于 WebAssembly SIMD 提案更是如此。
SIMD 是一个单指令多数据流。它允许一条指令同时操作多个数据项。因此,大多数现代 CPU 都支持矢量运算的某些子集。因此,该提案正尝试利用你每天使用的硬件中已经存在的功能。
这里的挑战是找到一个在大多数架构中都能得到良好支持的子集,但仍在寻找过程中。
目前,该子集仅限于 128 位 SIMD。有两种不同方式去使用 SIMD 提案。
通过使用自动矢量化,你可以在编译时传入一个标志位来启用 SIMD,并且编译器会自动识别你的程序。
另一方面,很多 SIMD 用例都比较小众,即为了高度专注性能使用手动编写 assembly。
所以,这些将会使用 Clang 内置或内联函数生成针对性能调优的机器码。(译者注:Clang 是一个 C 语言、C++、Objective- C 语言的轻量级编译器)
现在,SIMD 可用于各种各样的应用程序。所以你可以用它来制作图像或音频,视频编解码器,像 Google Earth 和 Photoshop 这样的应用程序,甚至是 Web 上的机器学习应用程序。
WebML 与 SIMD
我们对 WebML(Web 建模语言)和 SIMD 的协作也很感兴趣。
那么让我们来仔细看看这些数据是如何运作的。
在这里,你可以看到一个关于数组 add 指令的简单示例。
假设这是一个 Integer 类型的数组,在左侧,看起来像是标量运算,将每个数字与另一个数字相加并存储结果。这个矢量版本只能归结为一条硬件指令。(译者注:从图中可以看到有箭头方向)例如,在一些 Intel 架构上的一个 p AD 或一个 vp AD 操作。
因此,SIMD 操作通过允许将多块数据打包到一个数据域中来工作,并且使指令能够作用于每个数据块。
这对于必须对大量数据执行相同操作的情况很有用。
例如,图片处理。如果你想在 Squoosh 中压缩图片,或者将 Photoshop 中图片的色彩降低一半。实际上,SIMD 操作在做这些事情时性能会更高。
我们已经讨论了关于利用底层硬件功能来提升 OA 办公应用程序的性能。现在,让我们来看看另一方面会发生什么。那么我们该如何更好地与主机交互呢?
引用类型提案
其中一个提议是引用类型提案,由多个浏览器实现。对于引用类型提案,WebAssembly 代码可以使用 ref 值类型传递任意的 JavaScript 值。这些值对 WebAssembly 不具备透明性,但通过导入 JavaScript 内置函数,WebAssembly 模块可以执行许多基本的 JavaScript 操作,而无须本来需要的 JavaScript 胶水代码。
所以,WebAssembly 表对象是一个高级别存储函数引用的结构。所以这个引用类型提议也添加了一些用于操作 wasm 内表的表指令。关于这一点的巧妙之处在于,引用类型提案实际上为未来真正有用的提案奠定了基础。
基于此,可以通过 Web IDL(接口定义语言)提案更高效的与本地主机进行交互操作或者异常引用的处理。同时它还可以实现更平滑的垃圾回收路径,我将在接下来的几张幻灯片中讨论这些。
WebIDL 绑定提案
因此,我们团队在不久的将来会侧重于一个 Web IDL 绑定提案(译者注:后面有图,一看便知)
Web IDL 是一个接口定义语言,它可以用来定义在 Web 中实现的接口。
我们通过引用类型来稍微谈下这个,这里的基本思路是,该提案描述了向 WebAssembly 添加一个新机制,为了避免在调用或者通过调用 Web IDL 接口时产生不必要的开销。
Web IDL 提案将会允许编译器优化从 WebAssembly 到现有的 Web API 和浏览器环境以及未来可能会用 Web IDL 的其他 API 的调用。
让我们仔细看下这张图。
当你有一个 wasm 函数时,你将会通过 JS API 调用 JavaScript。
JS API 通过绑定层来促成 JS API 与用于 Dom 访问的 Web API 之间的通信,这里增加了许多胶水代码和额外开销。
Web IDL 绑定提案的目标就是减少这种开销,并优化从 WebAssembly 到现有的 Web API 的调用。
所以实际上,这里不需要通过 JS API,并且绑定也将被优化用以减少开销。
因此,你可以精简 WebAssembly 和 Web API 之间调用。
目前,我们谈到了 C,C++,Rust,将这些语言放到 WebAssembly 上时,都得到了非常好的支持。而且有很多工作会不断地需要将不同种类的其他语言引入 Web 中来。
WebAssembly 的发展与未来
一旦对支持高级语言必须的垃圾回收功能得以实现,也就意味着更快的执行,更小的模块。
除 C,C++ 之外,这实际上能够支持大多数现代语言的要求。
当然,这也是个巨大的开放性问题。但是我们一直通过制定较小的提案并进行精确的设计约束磨练探索,以此来取得进展。
因此,目前,wasm 被明确地设计用于位尾调用优化。将来,我们希望为需要尾调用仿真(系统 API,参考前面的分享)的语言启用正确有效的尾调用实现。
所以这些就像是 Haskell 这样的函数式语言。
V8 已经实现了这个,并且进展得十分顺利。因此,对于完整的 C ++ 支持,我们需要异常处理。
在 Web 环境中,异常处理可以使用 JavaScript 进行模拟。
JavaScript 可以为异常处理提供正确的语义,但它的确不快。
因此发布 MVP 后,WebAssembly 将会获得 0 成本的异常处理支持,这也是我们正在努力做的事。
我们也在研究一些其他提案,所以请随时查看在 WebAssembly GitHub 页面上的未来特性文档。
我要强调的另一件事就是其中有很多都还在设计阶段,如果你有兴趣参与,所有的开发都是在一个开放的社区小组中完成的,因此,随时欢迎贡献。
我们也会讨论性能。如果你有性能瓶颈,并且使用了 wasm 去提升了其中一些我们所关注的点,我们会很高兴听取你的意见。
好了,Surma 和我就讲到这里。之后我们会在 Web Sandbox 继续。因此,如果你有问题,请过来找我们或者在网上找我们。
联系我们
欢迎加群和我们交流
如果感兴趣可以关注我们的微信公众号