这是关于 WebAssembly 及其速度优势的系列文章的第六部分。如果您还没有阅读过其他文章,我们建议您从 开头 开始。
2 月 28 日,四大主要浏览器 宣布达成共识,WebAssembly 的 MVP 已完成。这提供了一个稳定的初始版本,浏览器可以开始发布。
这提供了一个稳定的核心,浏览器可以发布。这个核心并不包含社区小组计划的所有功能,但它提供了足够的性能和可用性,让 WebAssembly 变得快速。
有了它,开发者就可以开始发布 WebAssembly 代码了。对于早期版本的浏览器,开发者可以发送 asm.js 版本的代码。因为 asm.js 是 JavaScript 的子集,任何 JS 引擎都可以运行它。使用 Emscripten,您可以将同一个应用程序编译成 WebAssembly 和 asm.js。
即使在初始版本中,WebAssembly 也会很快。但它应该在未来通过修复和新功能的组合变得更快。
提高浏览器中的 WebAssembly 性能
随着浏览器改进其引擎中的 WebAssembly 支持,一些速度改进将会到来。浏览器供应商正在独立地处理这些问题。
JS 和 WebAssembly 之间的更快函数调用
目前,在 JS 代码中调用 WebAssembly 函数的速度比它应该快得多。这是因为它必须做一些叫做“弹跳”的事情。JIT 不知道如何直接处理 WebAssembly,所以它必须将 WebAssembly 路由到可以处理它的东西。这是引擎本身的一段缓慢的代码,它做了一些设置来运行优化的 WebAssembly 代码。
这可能比 JIT 直接处理它要慢 100 倍。
如果您将单个大型任务传递给 WebAssembly 模块,您不会注意到这种开销。但是,如果您在 WebAssembly 和 JS 之间有大量的来回传递(就像您在处理较小的任务时所做的那样),那么这种开销是显而易见的。
更快的加载时间
JIT 必须管理更快加载时间和更快执行时间之间的权衡。如果您花更多时间提前编译和优化,这会加快执行速度,但会减慢启动速度。
正在进行大量工作来平衡提前编译(这确保了代码一旦开始运行就不会出现卡顿)和基本事实,即大多数代码部分不会运行足够多的次数来使优化值得。
由于 WebAssembly 不需要推测将使用哪些类型,因此引擎不必担心在运行时监控类型。这给了它们更多选择,例如将编译工作与执行并行化。
此外,对 JavaScript API 的最新添加将允许 WebAssembly 的流式编译。这意味着引擎可以在下载字节时开始编译。
在 Firefox 中,我们正在开发一个双编译器系统。一个编译器将提前运行并对代码进行相当好的优化。在运行代码的同时,另一个编译器将在后台执行完全优化。完全优化的代码版本将在准备好时被替换。
向规范添加 MVP 后功能
WebAssembly 的目标之一是按小块进行规范并在过程中进行测试,而不是预先设计所有内容。
这意味着有很多功能是预期的,但还没有被完全考虑。它们必须经过规范流程,所有浏览器供应商都积极参与其中。
这些功能被称为未来功能。这里只列举几个。
直接与 DOM 交互
目前,没有办法与 DOM 交互。这意味着您无法执行像 element.innerHTML
这样的操作来从 WebAssembly 更新节点。
相反,您必须通过 JS 来设置值。这意味着将值传递回 JavaScript 调用者。另一方面,它可以意味着从 WebAssembly 中调用 JavaScript 函数——JavaScript 和 WebAssembly 函数都可以用作 WebAssembly 模块中的导入。
无论哪种方式,通过 JavaScript 的方式可能比直接访问慢。WebAssembly 的一些应用程序可能会被推迟,直到这个问题得到解决。
共享内存并发
加速代码的一种方法是使其能够将代码的不同部分并行运行,即同时运行。但这有时会适得其反,因为线程之间通信的开销可能比任务本身花费的时间更多。
但是,如果您可以在线程之间共享内存,它会减少这种开销。为了做到这一点,WebAssembly 将使用 JavaScript 的新 SharedArrayBuffer。一旦它在浏览器中到位,工作组就可以开始规范 WebAssembly 如何与它们一起工作。
SIMD
如果您阅读其他帖子或观看有关 WebAssembly 的演讲,您可能会听到关于 SIMD 支持的内容。这个缩写代表单指令多数据。它是并行运行事物的一种方法。
SIMD 使得能够获取一个大型数据结构(如不同数字的向量),并同时将相同的指令应用于不同的部分。这样,它可以极大地加速游戏或 VR 所需的复杂计算类型。
这对普通 Web 应用程序开发者来说并不太重要。但对于使用多媒体(如游戏开发者)的开发者来说,它非常重要。
异常处理
许多像 C++ 这样的语言中的代码库使用异常。但是,异常还没有被指定为 WebAssembly 的一部分。
如果您使用 Emscripten 编译代码,它将为某些编译器优化级别模拟异常处理。不过,这很慢,所以您可能希望使用 <a href="https://kripken.github.io/emscripten-site/docs/optimizing/Optimizing-Code.html#c-exceptions">DISABLE_EXCEPTION_CATCHING</a>
标志将其关闭。
一旦异常在 WebAssembly 中得到原生处理,这种模拟就不再需要了。
其他改进——让开发者更容易
一些未来的功能不会影响性能,但会使开发者更容易使用 WebAssembly。
- 一流的源代码级开发工具。目前,在浏览器中调试 WebAssembly 就像调试原始汇编一样。虽然很少有开发者能够在脑海中将他们的源代码映射到汇编,但我们正在研究如何改进工具支持,以便开发者能够调试他们的源代码。
- 垃圾收集。如果您可以在预先定义类型,您应该能够将您的代码转换成 WebAssembly。因此,使用 TypeScript 之类的代码应该可以编译成 WebAssembly。不过,目前唯一的问题是 WebAssembly 不知道如何与现有的垃圾收集器交互,比如内置在 JS 引擎中的垃圾收集器。这个未来功能的想法是让 WebAssembly 通过一组低级的 GC 原语类型和操作来获得对内置 GC 的一流访问权限。
- ES6 模块集成。浏览器目前正在添加使用
script
标签加载 JavaScript 模块的支持。一旦添加了此功能,像<script src=url type="module">
这样的标签即使 url 指向 WebAssembly 模块也能正常工作。
结论
WebAssembly 现在很快,随着浏览器中新功能和改进的实施,它应该会变得更快。
关于 Lin Clark
Lin 在 Mozilla 的高级开发部门工作,专注于 Rust 和 WebAssembly。
20 条评论