前言
在上一篇中我们讲了SIMD技术的基础和前世今生,可以结合上一篇文章一起看大前端CPU优化技术--SIMD技术。今天我们全局性地讲解下NEON技术。
目前主流的移动设备以ARM v7和v8版本架构占据了绝大多数的江山,随着近几年64位手机的普及和各大应用市场的强推下,64位的v8呈崛起之势。
ARM在ARMv5 架构开始引入 VFP(vector-floating-point) 指令扩展,可以通过使用短向量指令来加速浮点计算。
armv6架构中已经提出了一些simd的指令,可将多个16bit,8bit的数据加载到32bit寄存器中,但是并没有单独的执行单元 ,也没有单独的流水线。指令名字就是在之后加16或8的后缀。
armv7开始引入了advanced SIMD,定义了自己的向量寄存器,32*64bit register file,自己的流水线执行单元。这些SIMD的扩展被称为NEON。NEON
在32-bit内核的处理器上,如Cortex-A系列,如果不采用SIMD则会将大量时间花费在处理8-bit或16-bit的数据上,但是处理器本身的ALU、寄存器、数据深度又是主要为了32-bit的运算而设计的。因此NEON应运而生。
NEON就是一种基于SIMD思想的ARM技术,相比于ARMv6或之前的架构,NEON结合了64-bit和128-bit的SIMD指令集,提供128-bit宽的向量运算(vector operations)。
ARM NEON 技术本质上是一种高级的单指令多数据(SIMD)架构扩展,这种扩展仅在ARM Cortex-A 和 Cortex-R 系列处理器中使用。ARM NEON 单元默认包含在 Cortex-A7 和 Cortex-A15 处理器中,但在其他 ARMv7 Cortex-A 系列处理器中是可选的,某些实现 ARMv7--A 或 ARMv7--R 架构配置文件的Cortex-A 系列处理器可能不包含NEON单元。
NEON演进史
NEON 技术是依靠向量指令来加速计算,而鉴于 NEON 技术比VFP(vector-floating-point)提供的向量技术加速效果体验更优秀,从 ARMv7 架构开始使用 VFP 向量指令加速的模式被弃用。
在ARMv7中,NEON 与 VFP 指令集具有以下关系:
- 具有 NEON 单元但没有VFP单元的处理器无法在硬件中执行浮点运算。
- 由于 NEON SIMD 操作更有效地执行向量计算,因此从 ARMv7 的引入开始,VFP 单元中的向量模式操作已被弃用。因此,VFP 单元有时也称为浮点单元(FPU)。
- VFP 可以提供完全兼容 IEEE-754 的浮点运算,ARMv7 NEON 单元中的单精度运算不完全符合 IEEE-754。
- NEON不能取代 VFP。VFP 提供了一些在 NEON 指令集中没有等效实现的专用指令。
- 半精度指令仅适用于包含半精度扩展的 NEON 和 VFP 系统。
- 在ARMv8中,VFP已被NEON取代,以上问题如 NEON 并不完全符合 IEEE 754 标准,并且有一些指令 VFP 支持而 NEON 不支持的问题已在 ARMv8 中得到解决。
再加上NEON本身也仅在部分处理器上才支持,所以必须首先确认处理器是否支持 NEON 和 VFP。可以在编译和运行的时候进行检查。需要注意的是neon可以和vfp同时使用,但由于寄存器是公用的。 ARMv7 有 32 个 NEON D 寄存器,如下图所示
编译/运行时检查
编译期可在 ARM 编译器工具链(armcc)v4.0 及更高版本或 GCC 中,检查预定义宏 ARM_NEON 或者 __arm_neon 是否开启。
运行时检测 NEON 单元需要依赖操作系统的能力,由于 /proc/cpuinfo
输出是基于文本的,因此通常首选查看辅助向量 /proc/self/auxv
,其包含二进制格式的内核 hwcap ,可以轻松地在 /proc/self/auxv
文件中搜索 AT_HWCAP 记录,以检查 HWCAP_NEON 位(4096)。
NEON架构
熟悉 ARMv7-A 架构的应该知道 ARMv7 架构的内核是一个32位的系统,使用32位的寄存器。但是 NEON 单元使用的是64位或者128位的寄存器。这里的原因就是 NEON 单元使用独立的寄存器文件。不过,NEON 单元还是完全集成到处理器中的,可以和处理器共享整型操作单元、循环控制和缓存资源,相比于使用硬件加速器,大大降低了面积和功耗成本。而且由于 NEON 单元和应用程序使用相同的地址空间,可以使用更简单的编程模型。
ARM NEON 技术的核心是 NEON 单元,主要由四个模块组成,分别是 NEON 寄存器文件、整型执行流水线、单精度浮点执行流水线和数据加载和重排流水线。
The components of the unit are:
- Neon register file
- Neon integer execute pipeline
- Neon single-precision floating-point execute pipeline
- Neon load/store and permute pipeline
NEON 寄存器
NEON 寄存器主要是用来存放包含相同数据类型元素的向量。在 ARMv7 架构中, 一共有16个128位寄存器,这个128位寄存器也称之为 Q 寄存器,一个128位寄存器又可以分为两个64位寄存器,即一共有32个64位寄存器,64位寄存器又称之为 D 寄存器。在ARMv8 架构中寄存器的数量相比 ARMv7 架构数量翻倍。Q 寄存器和 D 寄存器对应表如下所示:
注:每一个Q0-Q15寄存器映射到一对D寄存器。
寄存器之间的映射关系:
- D<2n> 映射到 Q 的最低有效半部;
- D<2n+1> 映射到 Q 的最高有效半部;
结合NEON支持的数据类型,NEON寄存器有如下图的几种形态:
NEON指令执行
上图为 NEON 单元完成加速计算的流程图。其中向量寄存器中的每个元素同步执行计算,以此来加速计算过程。
NEON指令类型
NEON指令按照操作数类型可以分为正常指令、宽指令、窄指令、饱和指令、长指令。
-
正常指令:生成大小相同且类型通常与操作数向量相同到结果向量。
-
长指令:对双字向量操作数执行运算,生成四字向量结果。所生成的元素一般是操作数元素宽度到两倍,并属于同一类型。用L标记,如VMOVL。
-
宽指令:一个双字向量操作数和一个四字向量操作数执行运算,生成四字向量结果。用W标记,如VADDW。
-
窄指令:四字向量操作数执行运算,并生成双字向量结果,所生成的元素一般是操作数元素宽度的一半。用N标记,如VMOVN。
-
饱和指令:当超过数据类型指定的范围则自动限制在该范围内。用Q标记,如VQSHRUN
NEON指令调用
ARM 平台提供了四种使用 NEON 技术的方式,分别为 NEON 内嵌函数、NEON 开源库、编译器自动向量化和 NEON 汇编。
NEON内嵌函数
NEON 内嵌 函数提供了一种编写 NEON 代码的方法,该方法比汇编代码更易于维护,同时仍然可以控制生成的 NEON 指令。
内部函数使用与 D 和 Q NEON 寄存器对应的新数据类型。数据类型支持创建直接映射到NEON 寄存器的 C 变量。
NEON 内嵌函数调用类似于普通函数调用,通过调用函数接口告知编译器需要优化的代码,编译器在编译阶段直接使用 NEON指令替换这些内嵌函数而不是执行类似子函数调用的操作。NEON 内嵌函数提供了一种低级的 NEON 指令访问方式,编译器做了一些通常与编写汇编语言相关的繁重工作来达到获取最高性能的目标,例如:
- 寄存器分配
- 代码调度或重新排序指令
NEON 内嵌函数的缺点在于无法让编译器准确输出想要的代码,因此在转向NEON汇编代码时仍有一些改进的可能性。
自动矢量化
向量化编译器可以将C或C++源代码进行矢量化,以实现对NEON硬件的有效使用。这意味着尽管开发者开发可移植的C或C++源码,也可能通过矢量编译器的自动向量化功能享受到NEON指令带来的性能提升。GCC和ARM编译器均能通过配置编译选项开启自动向量化,从而使用NEON技术优化代码。如果编译器能够确定开发者的意图,矢量化编译器可以以最优的方式优化代码。相较于针对某一特定处理器高度调整的代码,易于人们理解的简单代码更容易向量化。
NEON开源库
鉴于 NEON 指令的强大优化效果,市场上出现了很多支持 NEON 优化的开源库,比如 Ne10、OpenMAX、ffmpeg、Eigen3和Math-neon等
NEON汇编
对于性能要求特别高的程序,手工编写汇编代码是更适合的方式。
GNU 汇编器(gas) 和 ARM Compile r工具链汇编器(armasm)都支持 NEON 指令的汇编。
其他平台上的 SIMD 技术
SIMD 处理不是 ARM 独有的,下图将其与 x86 和 Altivec 进行了比较。
专用 DSP 对比
许多基于 ARM 的 SOC 中还包含 DSP 等协处理硬件,因此可以同时包含 NEON 单元和DSP。相对于 DSP,NEON 的特点有 :
- 扩展了ARM 处理器流水线
- 使用 ARM 内核寄存器进行内存寻址- 简易的开发和调试
- SMP 能力。MPCore 处理器中的每个 ARM 内核都有一个 NEON 单元。
- 开源社区和ARM生态系统都提供了广泛的 NEON 工具支持
DSP特点:
- 与 ARM 处理器并行运行
- 与 ARM 处理器集成度较低。在 DSP 和 ARM 处理器之间传输数据可能会有一些缓存清理或刷新开销。
总结
本文主要介绍了NEON的基本原理、寄存器、指令调用等基础知识,但NEON 技术所能探讨的内容远不止于此,后续也将会更深入的研究。欢迎转发、关注、收藏。 微信公众号首发,欢迎关注、转发、收藏,感谢支持。