【WebAssembly 的未来】成长技能树(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hj7jay/article/details/84935509

人们对WebAssembly有些误解。他们认为在2017年登陆浏览器的WebAssembly - 我们称之为WebAssembly的最小可行产品(或MVP) - 是WebAssembly的最终版本。

我可以理解这类误解来自何处。WebAssembly社区组实际上致力于后向兼容。这意味着你今天所创建的WebAssembly将继续在未来的某版浏览器上工作。

但这并不意味着WebAssembly是功能完整的。事实上,它远非如此。WebAssembly将会有许多功能,它们将从根本上改变你使用WebAssembly所能做的事情。

我认为这些未来的功能有点像电子游戏中的技能树。我们已经完全掌握了这些技能中的前几项,但我们还需要完善下面的整个技能树来解锁所有应用程序。

那么让我们看看已经存在的概念,然后我们就可以看到未来的将会有哪些新的东西。

WebAssembly故事的最开始于Emscripten,它可以通过将C ++代码转译为JavaScript实现了在Web上运行C++代码。这使得有可能将大量现有的C++代码库(如游戏和桌面应用程序)带入到Web中。

但它自动生成的JS仍然比作为对比的原生代码慢得多。但Mozilla工程师发现了一个隐藏在所生成的JavaScript中的类型系统,并给出了如何使JavaScript运行得飞快的方法。这个JavaScript子集被命名为asm.js。

其他浏览器供应商看到asm.js的速度有多快之后,他们也开始向他们自己的引擎中添加同样的优化处理。

但这并不是故事的结束。这只是一个开始。引擎自身可以做些事情使其运行更快。

但他们无法在JavaScript本身中做到这一点。因此,他们需要一种新的语言 - 专门为编译而设计的语言。这就是WebAssembly。

那么第一版WebAssembly需要哪些技能呢?我们需要什么才能实现可以在网络上有效运行C和C++的最小可行产品呢?

技能: 编译目标

使用WebAssembly的人知道他们不想仅支持C和C++。他们希望能够将许多不同的语言编译为WebAssembly。所以他们需要一个与语言无关的编译目标。

他们需要类似汇编语言这样的东西,像桌面应用程序可被编译成x86一样。但这种汇编语言不适用于实际的物理机。这将是一个概念机。

技能: 快速执行

编译目标必须被设计,以便它可以非常快地运行。否则,在Web上运行的WebAssembly应用程序将无法满足用户对平滑交互和游戏的期望。

技巧: 紧凑

除了执行时间,加载时间也需要快。 用户对某些内容的加载速度有一定的期望。 对于桌面应用程序,由于应用程序已安装在您的计算机上,因此它们会快速加载。 对于 Web 应用程序,因为 Web 应用程序通常不必加载与桌面应用程序一样多的代码,用户也期望加载很快。

但是,当你将这两件事结合起来时,它会变得棘手。 桌面应用程序通常是相当大的代码库。 因此,如果它们在网络上,当用户第一次访问 URL 时,需要下载和编译很多内容。

为了满足这些期望,我们需要我们的编译器目标是紧凑的。 这样,它可以快速通过网络传输。

技能:线性内存

除了执行时间,加载时间也需要快速。用户对某些内容的加载速度有一定的期望。对于桌面应用程序来说,由于应用程序已安装在你的计算机上,因此对它们的期望是快速加载。对于 Web 应用程序,期望也是加载时间能够尽可能短,因为 Web 应用程序通常不必加载与桌面应用程序一样多的代码。

但是,当你将这两件事结合起来时,它会变得很棘手。桌面应用程序通常是相当庞大的代码库。因此,如果它们位于网络上,当用户第一次访问此 URL 时,需要下载和编译很多内容。

为了满足这些预期,我们需要我们的编译器目标是紧凑的。这样,它可以快速通过网络进行传输。

技能:线性内存

这些语言还需要能够以不同于 JavaScript 使用内存的方式使用内存。他们需要能够直接管理他们的内存 —— 能区分出哪些字节一起使用。

这是因为像 C 和 C++ 这样的语言有一个叫做指针的底层特性。你可以拥有一个没有值的,但具有该值的内存地址的变量。因此,如果你要支持指针,程序需要能够从特定地址进行写入和读取。

但是,你无法实现从网上下载的程序可无限访问内存中的字节,任意访问它们想访问的地址。因此,为了创建一种对内存访问的安全方式,就像使用原生程序做法一样,我们必须创建一些可以对特定区域内存访问的东西,并且别无它法。

为此,WebAssembly 使用线性内存模型。这是使用 TypedArrays 实现的。它基本上就像一个 JavaScript 数组,除了这个数组是仅包含字节的内存。当你访问其中的数据时,你只需使用数组索引,你可以将其视为内存地址。这意味着你可以假装这个数组是 C++ 中的内存。

已解锁成就

因此,使用所有这些技能,人们可以在浏览器中运行桌面应用程序和游戏,就好像他们在其计算机上本机运行一样。

这几乎就是 WebAssembly 作为 MVP 发布时的大致技能。它确实是MVP——最小可行产品。

它还支持某些类型的应用程序正常工作,但仍有许多其他技能可解锁。

重量级桌面应用程序

下一个解锁的成就是更重量级的桌面应用程序。

你能设想像 Photoshop 这样的应用程序在你的浏览器中运行的场景吗? 如果你可以像使用 Gmail 一样在任何设备上即时加载它呢?

我们已经开始见证这样的事情。例如,Autodesk 的 AutoCAD 团队已使其 CAD 软件在浏览器中可用。Adobe 已经使用 WebAssembly 通过浏览器提供了 Lightroom。

但这也有一些我们还需要增加的功能,以确保所有这些应用程序 —— 即使是最最重量级的应用在浏览器中也能运行良好。

技能: 线程化

首先,我们需要支持多线程。现代计算机拥有多核。这些基本上是多个可并行处理问题的大脑。这可以使事情完成更快,但是为了使用这些核,你需要支持线程。

技能: SIMD

除了线程之外,还有另一种使用现代硬件的技术,它可以让你并行处理事务。

那就是 SIMD:单指令多数据。使用 SIMD,可以占用大块内存,并切分到不同的执行单元,这类似于内核。然后你使用相同的代码 —— 在所有这些执行单元上运行相同的指令,但它们每个都将该指令应用于它们自己的数据位上。

技能: 64位寻址

WebAssembly 需要充分利用的另一个硬件功能是64位寻址。

内存地址仅仅是数字而已,所以如果你的内存地址长度只有32位,你只能拥有这么多内存地址 —— 足够4千兆字节的线性内存。

但是对于64位寻址,你有16E字节内存。当然,你的计算机中没有16E字节的实际内存。因此,其最大值取决于系统实际可提供的内存大小。但这将可以对 WebAssembly 的地址空间进行人为限制。

技能: 流式编译

对于这些应用程序,我们不仅需要它们快速运行。我们还需要其加载时间比目前更快。我们需要一些可用于提升加载时间的技能。

一个重要的步骤是进行流式编译 —— 在下载期间仍在编译 WebAssembly 文件。WebAssembly 专门设计了用于实现轻松的流式编译。在 Firefox 中,我们实际编译它的速度比其通过网络传入的速度快得多 —— 在下载文件完成时,它几乎完成了编译。其他浏览器也在添加对流式编译的支持。

另一件有用的事情是拥有一个分层编译器。

对于我们在 Firefox 中,这意味着有两个编译器。第一个,基线编译器 —— 在文件开始下载后立即启动。它可以非常快速地编译代码,以便其快速启动。

它所生成的代码执行速度很快,但并不是 100% 达到其最快速度。为了获得额外的性能,我们在后台的几个线程上运行另一个编译器 —— 优化编译器。这个编译需要更长的时间,但生成极快的代码。待其完成之后,我们将基线版本换成完全优化的版本。

这样,我们可以使用基线编译器实现快速启动,并使用优化编译器实现快速执行。

此外,我们正在开发一个名为 Cranelift 的全新的优化编译器。Cranelift 旨在通过从函数层级上并行快速编译代码。同时,它所生成的代码比我们当前的优化编译器具有更好的性能。

Cranelift 目前处于 Firefox 的开发版本中,但默认情况下已被禁用。一旦我们启用它,我们将更快地获得完全优化后的代码,并且代码将运行得更快。

但是我们可以使用更好的技巧来实现之,所以我们大多数时间都不需要编译了......

技能: 隐式 HTTP 缓存

使用 WebAssembly,如果在两个页面加载时加载相同的代码,它将编译为相同的机器代码。它不需要根据流经它的数据进行变化,就像 JS JIT 编译器需要的那样。

这意味着我们可以将编译后的代码存储在 HTTP 缓存中。然后当页面加载并转到获取 .wasm 文件时,它将仅从缓存中提取预编译的机器代码。这会完全跳过了你已访问过的缓存中的任意页面的编译。

技能: 其他改进

目前许多讨论正在围绕其他改进思路以及避免更多工作,因此请继续关注其他实时的改进信息。

我们目前处于哪一阶段?

我们目前支持哪些重量级应用程序?

线程化

关于线程化,我们已拥有一个做得不错的提案,但其中一个核心部分 —— SharedArrayBuffers 不得不于今年早些时候在浏览器中关闭。
这将会被重新打开。关闭它们只是一种临时措施,以减少在 CPU 中发现的并于今年早些时候关闭的 Spectre 安全问题的影响,目前已取得进展,敬请期待。

SIMD

SIMD 目前处于非常活跃的开发阶段。

64位寻址

对 wasm-64,我们很好地了解了如何添加它,它与 x86 或 ARM 如何支持64位寻址的实现非常类似。

流式编译

我们在2017年底添加了流式编译,并且其他浏览器也在为之努力中。

分层编译

我们在2017年底同时添加了基线编译器,并且其他浏览器在过去几年中也已添加了同样类型的架构。

隐式 HTTP 缓存

在 Firefox 中,我们对于实现隐式 HTTP 缓存的支持差不多接近收尾了。

即使这所有的功能仍在开发中,你会看到目前依然出现了一些重量级应用程序,因为 WebAssembly 已经为这些应用程序提供了所需的性能。

但是,一旦这些功能全部就位,这将是另一个已解锁的成就,这样更多类似的重量级应用程序将能够在浏览器中使用。

但 WebAssembly 不仅适用于游戏和重量级应用程序。它也适用于常规 Web 开发……对于那种 Web 开发人员习惯于:小模块类型的 Web 开发。

有时你的应用程序的一小部分会进行大量繁重的处理,并且在某些情况下,使用 WebAssembly 可以更快地完成此处理。我们希望将这些移植到 WebAssembly 中变得容易。

同样,这是其中一些已经发生的案例。开发人员已经将 WebAssembly 模块整合到那些有诸多繁重工作的小模块上。

一个例子是源映射(source map)库中的解析器,它在 Firefox 的 DevTools 和 webpack 中使用。它使用 Rust 重写过,编译为 WebAssembly,使其速度提高了11倍。并且在进行相同类型的重写后,WordPress 的 Gutenberg 解析器平均速度提高了86倍。

但是为了让这种用法真正普遍开来 —— 让开发者这样开发真正感到舒服,我们需要做更多的事情。

猜你喜欢

转载自blog.csdn.net/hj7jay/article/details/84935509