用于实现更快的 Web AI 的 WebAssembly 和 WebGPU 增强功能(第 1 部分)

了解 WebAssembly 和 WebGPU 增强功能如何提升 Web 上的机器学习性能。

Austin Eng
Austin Eng
Deepti Gandluri
Deepti Gandluri
François Beaufort
François Beaufort

基于 Web 的 AI 推断

我们都听过一个故事:AI 正在改变我们的世界。网络也不例外。

今年,Chrome 添加了生成式 AI 功能,包括创建自定义主题和/或帮助您撰写初稿。但 AI 的作用远不止于此;AI 本身可以丰富 Web 应用。

网页可以嵌入用于视觉(例如辨识面孔或识别手势)、用于音频分类或语言检测的智能组件。去年,我们见证了生成式 AI 的蓬勃发展,其中包括一些令人印象深刻的网络大语言模型演示。请务必查看面向 Web 开发者的实用设备端 AI

如今,大部分设备都支持 Web 上的 AI 推断,而 AI 处理可以利用用户设备上的硬件在网页中进行。

其功能强大,原因如下:

  • 降低成本:在浏览器客户端上运行推断可显著降低服务器成本,这对于生成式 AI 查询尤其有用,其费用可能比常规查询高几个数量级。
  • 延迟:对于对延迟特别敏感的应用(例如音频或视频应用),将所有处理任务都在设备上进行可以减少延迟。
  • 隐私保护:在客户端运行还有助于解锁一类需要加强隐私保护的新应用,这些应用无法向服务器发送数据。

目前,AI 工作负载如何在 Web 上运行

如今,应用开发者和研究人员使用框架构建模型,使用 Tensorflow.jsONNX 运行时 Web 等运行时在浏览器中执行模型,而运行时则利用 Web API 来执行。

所有这些运行时最终会通过 JavaScript 或 WebAssembly 在 CPU 上运行,或者在 GPU 上通过 WebGL 或 WebGPU 运行。

目前 AI 工作负载如何在 Web 上运行的示意图

机器学习工作负载

机器学习 (ML) 工作负载通过计算节点图推送张量。张量是这些节点的输入和输出,这些节点会对数据执行大量计算。

这一点非常重要,因为:

  • 张量是非常大的数据结构,对可能具有数十亿权重的模型执行计算
  • 扩缩和推断可以实现数据并行。这意味着对张量中的所有元素执行相同的操作。
  • 机器学习不需要精确率。你可能需要一个 64 位的浮点数才能登陆月球,但对于人脸识别,你可能只需要一个不超过 8 位的数字。

幸运的是,芯片设计人员添加了一些功能,使模型运行更快、更酷,甚至能运行模型。

与此同时,WebAssembly 和 WebGPU 团队正努力向 Web 开发者公开这些新功能。如果您是 Web 应用开发者,则不大可能经常使用这些低级基元。我们预计您正在使用的工具链或框架将支持新功能和扩展,因此只需对基础架构稍做更改即可受益。但如果您确实想要手动调整应用以提升性能,那么这些功能就与您的工作相关。

WebAssembly

WebAssembly (Wasm) 是一种紧凑、高效的字节码格式,运行时可以理解和执行。在设计上,它能够充分利用底层硬件功能,因此能以接近原生速度执行。代码经过验证,并在内存安全的沙盒环境中执行。

Wasm 模块信息用密集二进制编码表示。与基于文本的格式相比,这意味着解码速度更快、加载速度更快、内存用量更低。它具有可移植性,因为它不对现代架构中不常见的底层架构做出假设。

WebAssembly 规范是迭代的,在开放的 W3C 社区小组中负责制定。

二进制格式对承载环境没有假设,因此在非网络嵌入中也表现出色。

您的应用只需编译一次,即可在任何位置运行:台式机、笔记本电脑、手机或带有浏览器的任何其他设备。如需了解详情,请参阅编写一次,在最终通过 WebAssembly 实现的地方运行

笔记本电脑、平板电脑和手机的图示

大多数在网络上运行 AI 推断的生产应用都使用 WebAssembly,以便进行 CPU 计算以及与特殊用途的计算连接。在原生应用上,您可以访问通用和特殊用途的计算,因为应用可以访问设备功能。

在 Web 上,为了确保可移植性和安全性,我们会仔细评估提供的基元集。这可以在 Web 的可访问性与硬件提供的最佳性能之间取得平衡。

WebAssembly 是 CPU 的可移植抽象,因此所有 Wasm 推断都在 CPU 上运行。虽然这不是最优性能,但 CPU 适用广泛,并适用于大多数设备的大多数工作负载。

对于较小的工作负载(如文本或音频工作负载),GPU 将会很昂贵。最近有很多示例都适合使用 Wasm:

您还可以在开源演示中探索更多功能,例如:whisper-tinyllama.cpp在浏览器中运行的 Gemma2B

全面部署您的应用

您应根据具体的机器学习模型、应用基础架构和面向用户的总体预期应用体验来选择原语

例如,在 MediaPipe 的人脸特征点检测中,CPU 推理和 GPU 推理类似(在 Apple M1 设备上运行),但有些模型的方差可能显著增加。

对于机器学习工作负载,我们会考虑全面的应用视图,同时倾听框架作者和应用合作伙伴的反馈,以开发和交付用户迫切需要的增强功能。这些行为大致可分为三类:

  • 公开对性能至关重要的 CPU 扩展
  • 允许运行更大的模型
  • 实现与其他 Web API 的无缝互操作

计算速度更快

就目前而言,WebAssembly 规范仅包含我们向 Web 提供的一组特定指令。但是,硬件会继续添加新的指令,从而扩大原生性能和 WebAssembly 性能之间的差距。

请注意,机器学习模型并不总是需要高级别的精确率。“放松 SIMD”是一项提案,可降低一些严格的非确定性要求,从而加快某些对性能高的矢量运算的编码生成速度。此外,宽松的 SIMD 还引入了新的点积指令和 FMA 指令,可将现有工作负载的速度提高 1.5 到 3 倍。该版本已在 Chrome 114 中提供。

半精度浮点格式对 IEEE FP16 使用 16 位,而不是针对单精度值使用 32 位。与单精度值相比,使用半精度值有几个好处,可以减少内存要求,可以训练和部署更大的神经网络,还可以减少内存带宽。精度降低可加快数据传输和数学运算的速度。

大型模型

指向 Wasm 线性内存的指针表示为 32 位整数。这会产生两个后果:堆大小被限制为 4GB(当计算机的物理 RAM 远远超过该空间时),以 Wasm 为目标的应用代码必须与 32 位指针大小兼容(即 32 位指针大小)。

尤其是对于我们今天这样的大型模型,将这些模型加载到 WebAssembly 中可能会存在一定的限制。Memory64 提案消除了这些限制,因为线性内存大于 4GB,并且与原生平台的地址空间相匹配。

我们已在 Chrome 中全面实施,预计将于今年晚些时候发布。目前,您可以使用标志 chrome://flags/#enable-experimental-webassembly-features 运行实验,并向我们发送反馈

提升 Web 互操作性

WebAssembly 可以作为 Web 上的特殊用途计算的入口点。

WebAssembly 可用于将 GPU 应用引入 Web。这意味着,同一 C++ 应用可以在设备上运行,但只需略微修改即可在网络上运行。

Wasm 编译器工具链 Emscripten 已包含 WebGPU 的绑定。它是 Web 上 AI 推断的入口点,因此 Wasm 能够与 Web 平台的其余部分无缝互操作至关重要。我们正在这一领域研究几种不同的方案。

JavaScript promise 集成 (JSPI)

典型的 C 和 C++(以及许多其他语言)应用通常是针对同步 API 编写的。这意味着应用将停止执行,直到操作完成。与异步感知型应用相比,此类阻塞应用编写起来通常更直观。

当开销大的操作阻塞主线程时,它们可能会阻塞 I/O,并且卡顿对用户可见。原生应用的同步编程模型与网页的异步模型不匹配。这对于旧版应用尤其重要,因为它们的移植成本很高。Emscripten 提供了一种使用 Asyncify 执行此操作的方法,但这并非总是最佳选择,因为代码较大且效率低下。

以下示例计算斐波那契,使用 JavaScript promise 进行加法。

long promiseFib(long x) {
 if (x == 0)
   return 0;
 if (x == 1)
   return 1;
 return promiseAdd(promiseFib(x - 1), promiseFib(x - 2));
}
// promise an addition
EM_ASYNC_JS(long, promiseAdd, (long x, long y), {
  return Promise.resolve(x+y);
});
emcc -O3 fib.c -o b.html -s ASYNCIFY=2

在此示例中,请注意以下几点:

  • EM_ASYNC_JS 宏会生成所有必要的粘合代码,以便我们可以使用 JSPI 访问 promise 的结果,就像处理普通函数一样。
  • 特殊命令行选项 -s ASYNCIFY=2。这将调用 选项来生成使用 JSPI 与返回 promise 的 JavaScript 导入的接口。

如需详细了解 JSPI、使用方法及其优势,请参阅 v8.dev 中的 WebAssembly JavaScript Promise Integration API 简介。了解当前的源试用

内存控制

开发者对 Wasm 内存的控制非常少;该模块拥有自己的内存。任何需要访问此内存的 API 都必须复制进来,或者复制出去,而且这种用量会累加。例如,图形应用可能需要为每一帧执行复制操作。

内存控制方案旨在对 Wasm 线性内存提供更精细的控制,并减少整个应用流水线中的副本数。此方案处于第 1 阶段,我们在 V8(Chrome 的 JavaScript 引擎)中对此方案进行原型设计,以便为该标准的演变提供参考。

确定适合您的后端

虽然 CPU 无处不在,但不一定是最佳选择。在 GPU 或加速器上,专用计算性能可提高几个数量级的性能,对于较大的型号和高端设备而言尤其如此。这对于原生应用和 Web 应用都是如此。

选择哪种后端取决于应用、框架或工具链,以及影响性能的其他因素。尽管如此,我们将继续在提案上投入,让核心 Wasm 能够与其他网络平台良好地配合,更具体地说是与 WebGPU 配合使用。

继续阅读第 2 部分