如何使用 WebAssembly 和 JS构建高性能应用程序

本文最初发布于 Medium 网站 , 经原作者授权由 InfoQ 中文站翻译并分享 。
自计算机发明以来 , 原生应用程序的性能有了巨大的提升 。 相比之下 , 由于 JavaScript 最初并不是为提高速度而构建的 , 因此 Web 应用程序的运行速度曾经相当缓慢 。 但是 , 由于浏览器之间的激烈竞争以及 JavaScript 引擎(例如 V8)的迅速发展 , JavaScript 在机器上的运行速度也变得非常快了 。 但是它仍然无法在速度上击败原生应用程序 。 这主要是由于 JavaScript 代码必须经过多个流程才能生成机器代码 。
如何使用 WebAssembly 和 JS构建高性能应用程序文章插图
JS 引擎花费的平均时间
随着 WebAssembly 的引入 , 现代 Web 为我们所知的一切都有望迎来变革 。 这项技术快如闪电 。 在这篇文章文章中 , 我们就来看一下什么是 WebAssembly , 以及如何将它与 JavaScript 集成以构建高性能应用程序 。
什么是 WebAssembly?在深入了解 WebAssembly 之前 , 我们先来看一下什么是 Assembly 。
汇编(Assembly)是一种底层编程语言 , 与 CPU 架构的机器级指令有着非常紧密的联系 。 换句话说 , 它离机器可理解的代码(称为机器代码)只差一个转换过程 。 这种转换过程称为汇编 。
顾名思义 , WebAssembly可以理解为 Web 的汇编 。 它是一种类似于汇编语言的底层语言 , 有着紧凑的二进制格式 , 使你能够以接近原生的速度运行 Web 应用程序 。 它还为 C、C++ 和 Rust 等语言提供了编译目标 , 从而使客户端应用程序能够以接近原生的性能运行在 Web 上 。
此外 , WebAssembly 被设计为与 JavaScript 并存 , 而不是替代后者 。 使用 WebAssembly JavaScript API , 你可以在两种语言之间来回交换代码 , 而不会出现任何问题 。 这样 , 你就可以获得同时具备 WebAssembly 的功能和性能 , 以及 JavaScript 的多功能和适应性的应用程序 。 这打开了一个 Web 应用程序的全新世界 , 我们可以在 Web 上运行很多原本不准备用于 Web 的代码和功能 。
它带来了什么变化Lin Clark 预测 , 2017 年推出的 WebAssembly 可能会让 Web 开发产业迎来全新的拐点 。 上一个拐点来自现代浏览器中引入的 JIT 编译 , 其使 JavaScript 的速度提高了近 10 倍 。
如何使用 WebAssembly 和 JS构建高性能应用程序文章插图
JavaScript 性能
如果对比 WebAssembly 与 JavaScript 的编译过程 , 你会注意到前者剥离了几个步骤 , 剩下的都被缩减了 。 下图是两种语言编译过程的直观对比 。
如何使用 WebAssembly 和 JS构建高性能应用程序文章插图
WebAssembly 与传统 Web 应用程序编译过程的近似对比
仔细对比两者 , 你会注意到 WebAssembly 中的重优化部分已被完全剥离 。 这主要是因为:编译器无需对 WebAssembly 代码做任何假设 , 因为数据类型之类的东西在代码中是显式展现的 。
但 JavaScript 不是这样 , 因为 JIT 应该为运行代码做出假设 , 如果假设失败 , 则应重优化代码 。
如何获取 WebAssembly 代码WebAssembly 是一项伟大的技术 , 但是你该如何使用它的力量呢?
你有几种方法可用 。

  • 从头开始编写 WebAssembly 代码——除非你非常了解它的基础知识 , 否则完全不建议这样做 。
  • 从 C 编译为 WebAssembly
  • 从 C++ 编译为 WebAssembly
  • 从 Rust 编译为 WebAssembly
  • 使用 AssemblyScript 将 Typescript 的一个严格变体编译为 WebAssembly 。 对于不熟悉 C/C++ 或 Rust 的 Web 开发人员来说 , 这是一个不错的选项 。
  • Wasm 还支持更多语言选项 , 后文会提到 。
此外 , 还有 Emscripten 和 WebAssembly Studio 之类的工具可以帮助你完成上述过程 。
JavaScript 的 WebAssembly API为了充分利用 WebAssembly 的功能 , 我们必须将其与 JavaScript 代码集成在一起 。 这可以在 JavaScript WebAssembly API 的帮助下完成 。
模块编译和实例化WebAssembly 代码位于.wasm 文件中 。 该文件应编译为针对底层机器的机器码 。 你可以使用 WebAssembly.compile 方法来编译 WebAssembly 模块 。 收到已编译的模块后 , 可以使用 WebAssembly.instantiate 方法实例化已编译的模块 。 另外 , 你也可以将获取.wasm 文件获得的数组缓存传递到 WebAssembly.instantiate 方法中 。 这也可以 , 因为实例化方法有两个重载 。
【如何使用 WebAssembly 和 JS构建高性能应用程序】let exports;fetch('sample.wasm').then(response =>response.arrayBuffer();).then(bytes =>WebAssembly.instantiate(bytes);).then(results =>exports = results.instance.exports;});