1.3 对比其他Arm SIMD/DSP特性

Arm架构拥有超过30年的持续演进历史,在此期间,SIMD、矢量运算和DSP等运算方法被运用被在许多特性和扩展里。这里将简要回顾一下这些特性和扩展并展示Helium如何嵌入整个框图中。

在2004年,Arm Cortex-A8 CPU是第一款包含了Armv7-A先进SIMD扩展属性(即人们熟悉的Arm商标Neon)的CPU。添加SIMD扩展的主要目的是加速在CPU上面运行的媒体处理算法。在Armv8-A系列中,随着架构迁移到64位(AArch64)后,Neon增加了许多特性,其中包含完整的IEEE双精度浮点、64位整型运算以及更多的寄存器组(32个128位矢量寄存器)。

Cortex-A CPU通常要比本书中介绍的Cortex-M CPU大好几个数量级(就硅芯片面积和门数而言)。Cortex-A CPU通常配有很宽又很快的总线接口来访问缓存,这些接口允许在单个周期内一次读(或写)128位的数据。相比而言,Cortex-M处理器通常不会配有缓存,而且可能将数据存储在芯片内的静态随机存储器(Static RAM,SRAM)中,并且只有32位总线可用。此外,乘法器电路占用了相对较大的芯片面积和功耗/能耗。为了能够高效使用内存数据路径和乘法器,应该设计架构使得这两个模块尽可能频繁地被使用。这也就意味着简单地将Neon添加到Cortex-M处理器中并不是最优选择。

为了能在很小的处理器中获得高效的信号处理性能,并达到一定的性能点,在最初设计Helium时就考虑到了这些约束条件。它提供了许多新的架构特性来支持以前不太可能在Cortex-M设备上运行的应用程序。然而,正如我们所看到的一样,Helium和Neon存在许多共同特征。

1.3.1 Helium对比Neon

Neon是A系列处理器的架构扩展,其中包括Arm Cortex-A和Neoverse,它们都提供了高性能的SIMD能力。Helium和Neon有许多相似的地方,例如,Helium和Neon均使用FPU中的寄存器作为矢量寄存器,两者均使用128位矢量并且许多矢量处理指令也是这两种架构共用的。

然而,Helium是一种针对小型处理器实现高效信号处理性能的全新设计。它提供了许多专门针对嵌入式用例的新架构特性,因为它针对芯片面积(成本)和功耗进行了优化,所以赋予了M系列架构类似Neon的能力(通过Cortex-A的SIMD指令)。

优化后的Helium可以有效利用较小的Cortex-M内核中的所有可用硬件。它的矢量寄存器数量较少,一些操作可以同时使用矢量寄存器以及来自整型内核中标准的寄存器组R0~R14的标量值。Neon也能够使用标量值来执行矢量运算,但标量值使用的是FPU寄存器组。本书后面介绍原语函数时将会提到Helium原语及Neon原语均使用'_n_'修饰符来修饰带有标量值的矢量运算。

Helium支持半精度浮点(FP16)数据类型的运算,但并非所有Neon都支持FP16(Armv8.2-A架构为最新的Cortex-A处理器引入了FP16)。此外,Helium还具有其他特性,包括离散-聚合内存访存、支持复数运算、循环预测、通道预测以及许多额外的标量和矢量指令。

表1-1中总结了两个指令集之间的主要异同点。(Armv7-A和Armv8-A的Neon在寄存器数量和其他功能方面存在一些差异。)

表1-1 Helium对比Neon

Neon有一些基于微架构的复杂指令(例如,计算平方根的VSQRT指令),而对于Helium而言,支持这些复杂指令需要的硅片更大。其他不支持的指令包括VBIT、VCNT、VRECP、VSWP、VTBL、VTRN以及VZIP,但Helium允许更高效地模仿这些指令操作。例如,寄存器中的数据排列可以通过离散-聚合的方式实现,迭代的牛顿-拉弗森方法可用于快速得到矢量平方根/倒数。

1.3.2 Helium对比可伸缩矢量扩展

2016年,Arm推出了适用于Armv8-A架构的可伸缩矢量扩展(Scalable Vector Extension,SVE)。这显著提升了在AArch64(64位)执行状态下架构的矢量处理能力。硬件实现可以支持的矢量长度从128位扩展到2048位。SVE是对Neon的补充(不是替代品),它主要针对高性能计算(High Performance Computing,HPC)科学工作负载的场景。量子物理、天文学、气候科学、流体动力学和药物研究等领域的应用均可以利用极其强大的计算系统。SVE增加了许多特性,它们允许矢量化的编译器在并行化现有代码方面做得更好。

SVE允许CPU设计人员选择矢量长度,每个矢量寄存器的长度可以从128位到2048位不等。SVE支持矢量长度待定(Vector-Length Agnostic,VLA)的编程模型,该模型可适配为可用的矢量长度,以便用户可以为SVE一次性编译代码,然后在不同性能点的实现上运行它。

显然,超级计算机相对于Helium应用场景下的嵌入式系统有非常不同的需求,但Helium和SVE有几个共同点,包括:

• 聚合加载和离散存储。

• 按通道预测。这允许对复杂的控制代码进行矢量化,并减少循环头部和尾部的串行化。

• 预测驱动的循环控制和管理。这同样减少了矢量化开销。

1.3.3 Helium对比Cortex-M的DSP特性

实现架构Armv7-M(或更高版本)的Cortex-M处理器包括一组SIMD指令集。这使它们能够在某些应用中取代独立运行的DSP。

Cortex-M4、Cortex-M7、Cortex-M33和Cortex-M35P处理器提供对8位或16位整数进行操作的SIMD指令。这类指令利用CPU中的标准寄存器组,不像Helium,它使用一组单独的8个128位寄存器。这些标准整型内核寄存器(R0、R1等)均为32位宽,但SIMD指令对32位寄存器中的2个16位值或4个8位值进行操作。这意味着每条指令最多有4个操作(相比之下,Helium则有16个)。

正如我们将在本书中看到的,8位或16位数据操作对于处理音频或视频数据很有用,它们可能不需要完整的32位精度。Armv7-M中的SIMD指令针对特定的算法,不像Helium是高度正交的并且是一个很好的编译器目标。可以使用这些处理器上的可选FPU来加速浮点运算,但这并不提供SIMD或矢量运算。表1-2中总结了这些差异。

表1-2 Helium对比Armv7-M的SIMD

1.3.4 Helium对比专用DSP

许多当前系统使用独立的可编程DSP模块。在详细了解Helium之前,有必要介绍一下DSP的主要特性。DSP通常设计为能够实现计算和内存访问的并行执行(这可能需要哈佛式的存储器接口,将之连接到单独的指令内存和数据内存)。它们通常提供单周期乘加(Multiply-Accumulate,MAC)指令、零开销循环、分数和饱和运算、循环缓冲区(或循环内存寻址模式)以及具有一个或多个“保护位”的累加器。

“保护位”的概念可能并不为所有读者所熟悉。在信号处理和定点运算中,它们用于避免累加时溢出。

既包含CPU又包含DSP的系统具有独立运行各个模块的优势。通过为DSP子系统提供最大的数据吞吐量,可以优化性能和功耗。但是,DSP通常需要更专业的编程技能,并且通常需要与处理器匹配的单独工具链。每个DSP系列都有自己的特性,因此当从一个DSP系统移动到另一个DSP系统时,需要重写大量不可移植的代码。