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

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

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

网络上的 AI 推断

我们都听说过这样一个消息:AI 正在改变我们的世界。网络也不例外。

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

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

如今,Web 中的 AI 推断适用于大部分设备,AI 处理可以充分利用用户设备上的硬件,在网页本身中进行。

这项功能非常强大,原因如下:

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

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

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

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

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

机器学习工作负载

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

这一点非常重要,因为:

  • 张量是非常大的数据结构,可对具有数十亿权重的模型执行计算
  • 扩缩和推断可能会导致数据并行处理。这意味着对张量中的所有元素执行相同的操作。
  • 机器学习不要求精确。着陆到月球可能需要 64 位浮点数,但您可能只需要大量 8 位或更少的浮点数来进行人脸识别。

幸运的是,芯片设计师添加了一些功能,使模型运行更快、更炫酷,甚至让模型完全可以运行。

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

WebAssembly

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

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

WebAssembly 规范是迭代规范的,并在开放的 W3C 社区小组中开发。

二进制格式对托管环境不作任何假设,因此它也被设计为在非网络嵌入中也运行良好。

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

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

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

在网络上,为了可移植性和安全性,我们会仔细评估公开了哪些基元。这可在 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 远大于 4GB 时),并且针对 Wasm 的应用代码必须与 32 位指针大小(这样)兼容。

尤其是对于像我们现在这样的大型模型,将这些模型加载到 WebAssembly 中会非常有限。Memory64 方案消除了这些限制,即使线性内存大于 4GB 并匹配原生平台的地址空间。

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

更好的网络互操作性

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

WebAssembly 可用于将 GPU 应用引入 Web。这意味着可以在设备上运行的相同 C++ 应用只需稍加修改,也可以在 Web 上运行。

Emscripten 是 Wasm 编译器工具链,已针对 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 阶段,我们正在 Chrome 的 JavaScript 引擎 V8 中对其进行原型设计,以便为该标准的发展提供相关信息。

确定适合您的后端

虽然 CPU 无处不在,但并非总是最佳选择。GPU 或加速器上的特殊计算功能可以提供高出一个数量级的性能,尤其是对于较大的模型和高端设备。原生应用和 Web 应用都是如此。

您选择的后端取决于应用、框架或工具链,以及影响性能的其他因素。尽管如此,我们将继续投资于提案,以使核心 Wasm 能够与 Web 平台的其他部分(尤其是 WebGPU)完美配合。

继续阅读第 2 部分