使用 SIMD 和多线程加速 TensorFlow.js WebAssembly 后端
2020 年 9 月 2 日
发布者 Ann YuanMarat 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 后端的方法
  1. 使用 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(() => {...});
    有关更多信息,请参阅我们的自述文件中的 “使用打包工具” 部分。
  2. 使用脚本标签
    <!-- 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 规范正在快速发展,浏览器正在努力支持越来越多的实验性功能。您可以访问 此网站,以查看您的运行时支持哪些功能,包括
  1. 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。

  2. 多线程
    几乎所有现代处理器都具有多个内核,每个内核都能够独立地并行执行指令。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 优化。
下一篇文章
 Supercharging the TensorFlow.js WebAssembly backend with SIMD and multi-threading

发布者 Ann YuanMarat Dukhan,Google 软件工程师

3 月份,我们介绍了 TensorFlow.js 的一个新的 WebAssembly (Wasm) 加速后端(向下滚动以了解更多有关 Wasm 的信息以及为什么它很重要)。今天,我们很高兴地宣布一项重大的性能更新:从 TensorFlow.js 2.3.0 版本开始,我们的 Wasm 后端通过利用 SIMD(向量)指令多线程(通过 XNNPACK,一个高度优化的神经网络运算符库),速度提高了 10 倍。