介绍 SIMD.js

SIMD 代表 单指令多数据,是指对多个数据元素同时执行操作。例如,SIMD 加法指令可以并行添加多个值。SIMD 是一个非常流行的技术,用于加速图形、音频、编解码器、物理模拟、密码学和许多其他领域的计算。

除了提供性能之外,SIMD 还可以降低功耗,因为它使用更少的指令来完成相同的工作量。

SIMD.js

SIMD.js 是 Intel、Google 和 Mozilla 正在为 JavaScript 开发的新 API,它引入了几种用于执行 SIMD 计算的新类型和函数。例如,Float32x4 类型表示 4 个打包在一起的 float32 值。API 包含用于对这些值一起操作的函数,包括所有基本算术运算,以及用于重新排列、加载和存储这些值的运算。其目的是让浏览器直接实现此 API,并提供利用底层硬件中 SIMD 指令的优化实现。

目前重点是支持使用 SSE 的 x86 平台和使用 NEON 的 ARM 平台。我们也对支持其他平台的可能性感兴趣,可能包括 MIPSPower 等。

SIMD.js 最初源自 Dart SIMD 规范,并且它正在快速发展,成为一个更通用的 API,并涵盖更多用例,例如需要更窄整数类型的用例,包括 Int8x16 和 Int16x8,以及饱和运算。

SIMD.js 是一个相当低级的 API,预计将在此之上编写库以公开更高级的功能,例如矩阵运算、超越函数等等。

除了在普通 JS 中可用之外,还有工作正在进行,将 SIMD.js 添加到 asm.js 中,以便它可以从 asm.js 程序中使用,例如由 Emscripten 生成的程序。在 Emscripten 中,SIMD 可以通过内置的自动矢量化、通用 SIMD 扩展新的(仍在增长)Emscripten 特定 API 来实现。Emscripten 还将实现流行的标头(例如 <xmmintrin.h>)的子集,并使用 SIMD.js API 的包装器,作为在某些情况下简化移植 SIMD 代码的额外方法。

当今的 SIMD.js

SIMD.js API 本身正在积极开发中。该 ecmascript_simd github 仓库 目前用作临时规范,并提供了一个 polyfill 实现,以提供该功能,尽管当然没有提供 SIMD API 在现有浏览器上的加速性能。它还包括一些 基准测试,这些基准测试也用作基本 SIMD.js 用法的示例。

要查看 SIMD.js 的实际运行情况,请查看 与 IDF2014 关于 SIMD.js 的演讲配套的演示页面

该 API 已提交给 TC-39,TC-39 已将其批准为第 1 阶段(提案)。工作正在进行中,为后续阶段做准备,这些阶段将涉及提出更接近最终 API 的内容。

Firefox Nightly 中的 SIMD.js 实现正在积极开发中。Internet Explorer 将 SIMD.js 列为 “正在考虑中”。还有一个 Chromium 分支中的原型实现

短 SIMD 和长 SIMD

SIMD 的用途之一是加速对大型数据数组的处理。如果您有一个包含 N 个元素的数组,并且您希望对数组中的每个元素执行大致相同的事情,则可以将 N 除以平台提供的任何 SIMD 大小,并运行该数量的 SIMD 子例程实例。由于 N 非常大,我将这类问题称为长 SIMD 问题。

SIMD 的另一个用途是加速对数据集群的处理。RGB 或 RGBA 像素、XYZW 坐标或 4×4 矩阵都是此类集群的示例,我将用这些类型的类型表示的问题称为短 SIMD 问题。

SIMD 是一个广泛的领域,短 SIMD 和长 SIMD 之间的界限并不总是清晰的,但在高层面上,这两种风格截然不同。即使用来描述它们的术语也出现了分裂:在短 SIMD 世界中,将标量值复制到向量值中每个元素的操作称为“splat”,而在长向量世界中,类似的操作称为“广播”。

SIMD.js 主要是“短”风格 API,非常适合短 SIMD 问题。SIMD.js 也可用于长 SIMD 问题,并且它仍然会比普通标量代码提供显著的速度提升。但是,它的固定长度类型不会实现当今一些 CPU 的最大性能,因此仍然有空间开发另一个解决方案来利用可用的性能。

可移植性和性能

SIMD.js 的许多部分都存在着一种自然矛盾,即希望拥有一个跨所有重要平台一致运行的 API,以及希望让 API 在每个平台上尽可能快地运行。

幸运的是,有一组核心操作在各种平台上非常一致。这些操作包括大多数基本算术运算,并构成 SIMD.js 的核心。在这一组中,几乎不会产生任何开销,因为许多相应的 SIMD API 指令直接映射到单个指令。

但是,还有许多操作在一个平台上运行良好,而在其他平台上运行不佳。这些可能会导致意外的性能下降。SIMD.js API 的当前方法是专注于可以用尽可能少的性能下降完成的事情。它还专注于提供可移植的行为。总之,其目标是确保在一个平台上运行良好的程序也很可能在另一个平台上运行良好,并且运行良好。

在 SIMD.js 的未来迭代中,我们预计将扩展范围,并包括更多功能以及查询底层平台功能的机制。类似于 WebGL,这将允许程序确定哪些功能可用,以便它们可以决定是回退到更保守的代码,还是禁用可选功能。

总体愿景

SIMD.js 将加速当今各种需要大量计算的应用程序,包括游戏、视频和音频处理、科学模拟等等,并且这些应用程序可以在 Web 上运行。应用程序可以直接使用 SIMD.js API,库可以使用 SIMD.js 公开应用程序可以使用的高级接口,而 Emscripten 将用流行的 SIMD 习语编译 C++,生成优化的 SIMD.js 代码。

展望未来,SIMD.js 将继续发展,提供更广泛的功能。我们希望最终在 SIMD.js 旁边提供一个长 SIMD 风格的 API,这两个 API 可以像 OpenCL 组合显式向量类型和底层编程模型的隐式长向量并行性一样协同工作。

关于 Dan Gohman

我在 Cray、Apple 和 Google 工作过,在各种环境中开发过几种不同的编译器。我目前是 Mozilla 研究团队的一员,主要从事 asm.js、SIMD.js 和 Emscripten 的工作。

Dan Gohman 的更多文章……

关于 Robert Nyman [荣誉编辑]

技术布道师和 Mozilla Hacks 编辑。发表关于 HTML5、JavaScript 和开放 Web 的演讲和博客。Robert 坚信 HTML5 和开放 Web,并且自 1999 年以来一直在从事 Web 前端开发工作 - 在瑞典和纽约市。他还会定期在 http://robertnyman.com 上发表博客,并喜欢旅行和结识新朋友。

Robert Nyman [荣誉编辑] 的更多文章……


10 条评论

  1. Peter Jensen

    精彩的文章。一个小小的修正:TC39 在 2014 年 7 月的会议上批准了这个提案进入第 1 阶段(提案)。

    2014 年 10 月 30 日 下午 09:37

  2. Peter Jensen

    Crosswalk(https://crosswalk-project.org/) 是一个用于构建混合应用程序的 HTML5 Web 运行时,也支持 SIMD.js API。Crosswalk 可通过 Intel 的 XDK(xdk.intel.com)获得。

    2014 年 10 月 30 日 下午 10:08

  3. AlejandroG

    我认为这很酷也很有用,但为什么不实现像 Douglas Crockford 提出的标准 DEC64 浮点数这样真正重要的事情呢?我的意思是,仍然在泥土上修路。

    2014 年 10 月 30 日 下午 12:12

    1. Rick Waldron

      首先,Doug Crockford 已经不再是 TC39 的成员了。其次,你要求的是与 SIMD 正交的。值类型和类型化对象已经为 ES7 进行了准备:http://www.slideshare.net/BrendanEich/value-objects http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects https://bugzilla.mozilla.org/show_bug.cgi?id=578700

      2014 年 10 月 31 日 下午 10:57

  4. Zac Bowling

    那么 threading.js 或 pthread.js 呢?

    2014 年 10 月 30 日 下午 15:48

    1. Luke

      你是说 web 工作者吗?或者是在 asm.js 中使用它们?

      2014 年 10 月 30 日 下午 20:04

  5. Ningxin Hu

    优秀的概述!
    供参考,您可以在 https://github.com/crosswalk-project/v8-crosswalk 中找到 v8 SIMD.js 原型的源代码。您可以在 https://drive.google.com/folderview?id=0B9RVWZYRtYFeOVlSMm1GdmZxM0k&usp=sharing 下载最新的 Chromium SIMD.js 构建。

    2014 年 10 月 30 日 下午 17:43

  6. Jonathan Ragan-Kelley

    ## SIMD.js 回复

    看到这种东西在真正的 Web 运行时中取得进展,我很高兴,但其中有一点让我非常担忧:你关于“短”和“长”SIMD 的观点,暗示着即使在显式大小的短向量编程模型中,也存在关于如何在当前硬件上最有效地使用它的错误假设。具体来说,多年来,跨应用程序级短向量的结构组件进行向量化并不是大多数代码中最有效的向量化策略。相反,最快的向量化策略几乎总是依赖于应用“长 SIMD”视图,使用逐个短向量迭代的方式对跨越大型数据并行维度(如像素数组的 x 坐标)的最内层循环进行向量化,以每循环迭代处理 4 或 8 个像素,并进行更多对 R/G/B 的计算,而不是每循环迭代处理一个像素,一次计算 RGB。

    这与近十年前 GPU 着色器实现中的“标量化”运动相呼应。GPU 着色语言原生暴露 3 向量和 4 向量类型,以及这些类型的点积等等,因为这些是图形代码中非常常见的操作。但是,它们*不会*利用这一点进行“短 SIMD”执行,因为这几乎从来不像简单地将这种风格的代码标量化(将 dot(vec3, vec3) 操作转换为 3 个标量 MUL 和 2 个标量 ADD,或者一个标量 MUL 和 2 个标量 MAD,针对各个组件)并跨越更大的数据并行维度(单独的像素)那样有效。

    这并非特定于 GPU——它同样适用于 NEON 和 SSE 等短向量 SIMD 架构,更不用说 AVX 等大于 4 元素的短向量 SIMD 架构(或者应用于部分精度类型的 NEON/SSE)了。这是英特尔的 ISPC 所基于的执行策略,而且有充分的理由:即使在 SSE 上,它在很大程度上是为了处理 float32x4 向量而设计的,这也是绝大多数情况下正确的策略。

    为什么这种方式几乎总是更快,用图表解释起来最容易,但它仅仅是这些短向量中占用率的函数。即使代码跨 RGBA 和 XYZW 等结构维度进行向量化,并在给定架构上完全占用向量,但并非所有操作都将在完整的 4 向量上进行。相反,真实的代码——即使是在 3D 齐次坐标中进行几何变换——也会涉及 1、2、3 和 4 个组件向量的混合。如果我们将这些展开为标量操作,然后简单地跨 4 个相邻记录同时计算每个标量操作,我们将始终充分利用所有操作的 4 个向量通道(模分支),而“短 SIMD”版本仅在影响 1、2 或 3 个组件的任何操作中被部分利用。这与跨越更大数据并行维度的向量化往往会产生更多同质操作这一事实相关,这更好地利用了向量指令(你的算法想要以相同的方式处理 4 个相邻的红色值,比它想要在一个像素内以相同的方式处理所有红色、绿色、蓝色和 alpha 值更常见得多)。最终结果:“短 SIMD”视图你强调的这种方式,对于大多数代码来说,利用率明显更低,至少在 4 向量甚至是非常常见的应用程序数据类型的图形用例中是这样。

    从这种世界观开始你的设计,让我感到担忧,因为它会导致几乎只考虑向量化策略的例子,而这些策略在 上不会是最有效的。它还会让你专注于不同的特性:对向量条件/掩码值进行操作以及执行混合/预测对于“标量化”形式至关重要,但当向量仅编码 XYZ/RGB 类型元组时,它很少那么重要;同样地,如果你想象人们主要想打包 XYZ/RGB 向量,你会发现大于 4 元素的向量类型几乎没有用,而对于大多数代码来说,使用逻辑上的 512 位向量是完全合理的,然后将其编译为一些底层机器操作的固定数量。

    带着所有的尊重和乐观,我说这些,因为我真的希望能够针对这个目标!

    2014 年 10 月 30 日 下午 5:57

    1. 丹·戈曼

      确实。在假设的未来,如果 SIMD.js 在 SPMD 上下文中运行,我们很可能会切换实现策略。SIMD.js 可能会扮演今天图形语言中 vec3 等类型所扮演的角色,JIT 可能会选择将它标量化。在这样的系统中,将 SIMD.js 扩展到大于 4 元素的类型将是非常合理的。

      我们不知道 SPMD 是否会是答案,或者即使是,SPMD 内核语言是否会是 JS 本身。有很多可能性。我们知道的是,SIMD.js 在今天有几个重要的用例,并且它可以朝着几个方向发展,以满足各种未来的需求。

      2014 年 10 月 30 日 下午 7:56

  7. 汤姆

    我的华硕平板电脑搭载英特尔处理器,游戏运行速度很慢。
    我认为这是这里的一部分动机,我对此感到鼓舞。

    2014 年 11 月 4 日 上午 10:29

本文的评论已关闭。