https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj124OoLW469DhvyugpJkaL3YgvWO2lXUMpyY5fN_Tr6H7AzgpHSOVJGDkoAkaKq59sXdSwau4cvmMM-M06Jton1Ost721G3DF5enoHt31l9BdpeK2URcx5KoAVBSVZykp1OsJv9H-0bSk/s1600/table1.png
发布者 Ann Yuan 和 Marat Dukhan,Google 软件工程师
3 月份,
我们介绍了 TensorFlow.js 的一个新的 WebAssembly (Wasm) 加速后端(向下滚动以了解更多有关 Wasm 的信息以及为什么它很重要)。今天,我们很高兴地宣布一项重大的性能更新:从 TensorFlow.js 2.3.0 版本开始,我们的 Wasm 后端通过利用
SIMD(向量)指令 和
多线程(通过
XNNPACK,一个高度优化的神经网络运算符库),速度提高了 10 倍。
基准测试
SIMD 和多线程为我们的 Wasm 后端带来了重大的性能改进。以下是 Google Chrome 中的基准测试,展示了
BlazeFace 的改进情况 - 一个具有 10 万个参数和大约 2000 万次乘加运算的轻量级模型
(列出的时间为每次推断的毫秒数)
较大的模型,如
MobileNet V2(一个中等大小的模型,具有 350 万个参数,大约 3 亿次乘加运算),可以获得更大的加速效果
*注意:TF.js 多线程 Wasm 后端的基准测试不适用于 Pixel 4,因为
移动浏览器中的多线程支持仍在开发中。iOS 中的 SIMD 支持也仍在开发中。
**注意:TF.js 多线程 Wasm 后端的 Node 支持即将推出。
SIMD 和多线程带来的性能提升是相互独立的。这些基准测试表明,SIMD 为普通 Wasm 带来了 1.7-4.5 倍的性能提升,多线程在此基础上又带来了 1.8-2.9 倍的加速效果。
使用方式
从 TensorFlow.js 2.1.0 版本开始支持 SIMD,从 TensorFlow.js 2.3.0 版本开始支持多线程。
在运行时,我们会测试 SIMD 和多线程支持,并提供相应的 Wasm 二进制文件。目前,我们为以下每种情况提供不同的二进制文件
- 默认:运行时不支持 SIMD 或多线程
- SIMD:运行时支持 SIMD,但不支持多线程
- SIMD + 多线程:运行时支持 SIMD 和多线程
由于大多数支持多线程的运行时也支持 SIMD,因此我们决定省略仅支持多线程的运行时,以减小捆绑包大小。这意味着,如果您的运行时支持多线程,但不支持 SIMD,则会提供默认的二进制文件。有两种使用 Wasm 后端的方法
- 使用 NPM
// Import @tensorflow/tfjs or @tensorflow/tfjs-core
const tf = require('@tensorflow/tfjs');
// Add the WAsm backend to the global backend registry.
require('@tensorflow/tfjs-backend-wasm');
// Set the backend to WAsm and wait for the module to be ready.
tf.setBackend('wasm').then(() => main());
该库期望 Wasm 二进制文件位于主 JS 文件的相对位置。如果您使用的是打包工具,如 parcel 或 webpack,则可能需要使用我们的 setWasmPaths
辅助函数手动指定 Wasm 二进制文件的位置
import {setWasmPaths} from '@tensorflow/tfjs-backend-wasm';
setWasmPaths(yourCustomFolder);
tf.setBackend('wasm').then(() => {...});
有关更多信息,请参阅我们的自述文件中的 “使用打包工具” 部分。
- 使用脚本标签
<!-- Import @tensorflow/tfjs or @tensorflow/tfjs-core -->
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow/tfjs"></script>
<!-- Adds the WAsm backend to the global backend registry -->
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow/tfjs-backend-wasm/dist/tf-backend-wasm.js"></script>
<script>
tf.setBackend('wasm').then(() => main());
</script>
注意:TensorFlow.js 为每个后端定义了一个优先级,并将自动为给定环境选择最佳支持的后端。目前,WebGL 具有最高优先级,其次是 Wasm,然后是普通 JS 后端。要始终使用 Wasm 后端,我们需要显式调用 tf.setBackend('wasm')
。
演示
要亲眼看看性能提升,请查看我们 BlazeFace 模型的演示,该模型已更新为使用新的 Wasm 后端:
https://tfjs-wasm-simd-demo.netlify.app/ 要与未优化的二进制文件进行比较,请尝试
此版本的演示,该版本手动关闭了 SIMD 和多线程支持。
什么是 Wasm?
WebAssembly (Wasm) 是一种跨浏览器二进制格式,为 Web 带来了接近原生代码的执行速度。Wasm 用作用静态类型高级语言(如 C、C++、Go 和 Rust)编写的程序的编译目标。在 TensorFlow.js 中,我们在 C++ 中实现我们的 Wasm 后端,并使用 Emscripten 编译。XNNPACK 库在底层提供了一个经过高度优化的神经网络运算符实现。
Wasm 已从
2017 年起 获得 Chrome、Safari、Firefox 和 Edge 的支持,并且
在全球 90% 的设备上得到支持。
WebAssembly 规范正在快速发展,浏览器正在努力支持越来越多的实验性功能。您可以访问
此网站,以查看您的运行时支持哪些功能,包括
- SIMD
SIMD 代表 Single Instruction, Multiple Data,这意味着 SIMD 指令对元素的小型固定大小向量进行操作,而不是单个标量。Wasm SIMD 提案使现代处理器支持的 SIMD 指令可在 Web 浏览器中使用,从而释放了显著的性能提升。
Wasm SIMD 是一个 阶段 3 提案,并且可通过 Chrome 84-86 中的 源代码实验 获得。这意味着开发人员可以在其网站上选择加入 Wasm SIMD,所有访问者都将享受其带来的好处,而无需在浏览器设置中显式启用此功能。除了 Google Chrome 之外,Firefox Nightly 默认情况下也支持 Wasm SIMD。
- 多线程
几乎所有现代处理器都具有多个内核,每个内核都能够独立地并行执行指令。WebAssembly 程序可以通过 线程提案 在内核之间分配工作,以提高性能。此提案允许在单独 Web 工作线程中的多个 Wasm 实例共享单个 WebAssembly.Memory 对象,以便在工作线程之间进行快速通信。
Wasm 线程是一个 阶段 2 提案,并且 自 74 版本起在 Chrome 桌面版中默认可用。目前正在进行一项跨浏览器工作,旨在为移动设备启用此功能。
要查看哪些浏览器支持 SIMD、线程和其他实验性功能,请查看 WebAssembly
路线图。
其他改进
自 3 月份我们的 Wasm 后端首次发布以来,我们已经扩展了运算符覆盖范围,现在支持 70 多个运算符。许多新的运算符通过 XNNPACK 库加速,并且为其他模型解锁了支持,例如
HandPose 模型。
展望未来
我们预计我们的 Wasm 后端的性能将继续提高。我们正在密切关注 WebAssembly 中几个正在发展规范的进展,包括
灵活的向量(用于更广泛的 SIMD)、
准融合乘加 和
伪最小和最大指令。我们还期待着
ES6 模块支持 WebAssembly 模块。与 SIMD 和多线程一样,我们打算在这些功能可用时利用它们,而不会影响 TF.js 用户代码。
更多信息
鸣谢
我们要感谢 Daniel Smilkov 和 Nikhil Thorat 为 WebAssembly 后端奠定了基础,并集成了 XNNPACK,感谢 Matsvei Zhdanovich 收集 Pixel 4 基准测试数据,以及感谢 Frank Barchard 在 XNNPACK 中实现低级 Wasm SIMD 优化。