目录

  • NEON简介
    • SIMD是什么?
      • ARM SIMD 指令集
    • NEON是什么?
    • NEON架构概览
      • 支持的数据类型
      • NEON寄存器
      • NEON指令
    • NEON开发
      • 汇编器
      • Intrinsics
      • 自动向量化
      • 使用 NEON 优化库

NEON简介

这篇文章介绍了ARM NEON技术,这项技术首次实现是在ARM Cortex-A8 处理器上。这篇文章将讲述通用的SIMD概念以及NEON架构,并给出如何利用此技术的概要描述,包含以下章节:

  • SIMD是什么?
  • NEON是什么?
  • NEON的架构概览
  • NEON开发技术

SIMD是什么?

一些现代软件,尤其是多媒体编解码软件和图形加速软件,有大量的少于机器字长的数据参与运算。例如,在音频应用中16位以内数据是频繁的,在图形与视频领域8位以内数据是频繁的。

当在32位微处理器上执行这些操作时,相当一部分计算单元没有被利用,但是依然消耗着计算资源。为了更好的利用这部分闲置的资源,SIMD技术使用一个单指令来并行地在同样类型和大小的多个数据元素上执行相同的操作。通过这种方法,硬件可以在同样时间消耗内用并行的4个8位数值加法运算来替代通常的两个32位数值加法运算。

ARM SIMD 指令集

ARMv6 架构引入了SIMD指令集的一小部分,对打包到标准 32 位通用寄存器中的多个 16 位或 8 位值进行操作。这允许某些操作以两倍或四倍的速度执行,而无需实现额外的计算单元。这些指令的助记符通过将 8 或 16 附加到基本形式来识别,以指示操作的数据值的大小。

Figure1.1 展示了UADD8 R0,R1, R2指令操作。这个操作展示了以向量形式存储在通用寄存器R1R2中的4个8位数值的并行加法运算。最终结果也以向量形式存储到寄存器R0

Figure1.1 4-way 8-bit unsigned integer add operation

NEON是什么?

ARMv7 架构引入了高级 SIMD 扩展作为 ARMv7-A 和 ARMv7-R 配置文件的可选扩展。NEON通过定义存储在 64 位双字长的寄存器D 和128 位四字长的寄存器Q中的向量操作指令组来扩展 SIMD 概念。

用在ARM处理器上的高级SIMD扩展的实现称为NEON,这是架构规范之外使用的通用术语。NEON技术在当前所有ARM Cortex-A系列处理器上得到了实现。

NEON 指令作为 ARM 或 Thumb 指令流的一部分执行。相比使用额外的加速器,这简化了软件的开发,调试和集成。传统的ARM或Thumb指令管理所有程序流程和同步。NEON指令涉及以下管理:

  • 内存访问
  • NEON与通用寄存器之间的数据复制
  • 数据类型转换
  • 数据处理

Figure1.2 展示了VADD.I16 Q0, Q1, Q2 指令如何并行地执行存储在Q1,Q2中的8通道16位数值的加法运算,最终结果存储到了Q0。

Figure 1.2 8-way 16-bit integer add operation

NEON架构概览

ARM架构定义高级SIMD扩展作为协处理器10和11的一部分,协处理器10和11同时也用于向量浮点扩展(VFP)。虽然架构层面并不要求VFP和NEON同时实现,但是鉴于这些扩展在编程模型层面的共同特征,一个支持VFP的操作系统仅需很少甚至无需修改即可支持NEON。

当考虑对一个特定处理器进行NEON代码优化时,你可能不得不考虑处理器集成NEON技术的具体实现定义。这意味着即使 NEON 指令周期时序相同,针对特定处理器优化的指令序列在不同处理器上也可能具有不同的时序特征。

更多信息可以查看ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition 中关于高级SIMD扩展部分,其中包含了指令列表和编码。你可以在http://infocenter.arm.com获取有用信息。

支持的数据类型

NEON指令集支持8位,16位,32位和64位有符号和无符号整型。
NEON 还支持 32 位单精度浮点元素,以及 8 位和 16 位多项式。

VCVT指令用于单精度浮点元素与以下元素的类型转换:

  • 32位整型
  • 定点数
  • 半精度浮点,如果处理器实现了半精度扩展。

NEON寄存器

NEON 寄存器组由 32 个 64 位寄存器组成。 如果同时实现了 Advanced SIMD 和 VFPv3,那么它们将共享这个寄存器组。在这种情况下,VFPv3 以支持 32 个双精度浮点寄存器的 VFPv3-D32 格式实现。这种集成简化了上下文切换支持的实现,因为同一个例程既保存和恢复 VFP 上下文也保存和恢复 NEON 上下文。

NEON单元可以把同一个寄存器组看作:

  • 16个128位四字长寄存器组,Q0-Q15
  • 32个64位双字长寄存器组,D0-D31

NEON D0-D31寄存器和VFPv3 D0-D31寄存器是一样的,且每一个Q0-Q15寄存器都映射到一对D寄存器上。Figure1.3 展示了共享的NEON和VFP寄存器组的不同视图。所有这些视图都是随时可访问的。软件层面不需要显式地在他们之间切换,因为使用的指令决定了相应的视图。

Figure1.3 NEON and VFP register set

NEON指令

NEON指令仅提供了数据处理以及加载/存储操作,并集成到了ARM和Thumb指令集中。标准的ARM和Thumb指令管理了整个程序的控制流程。

NEON指令的编码相当于协处理器操作,和VFP指令一样,在协处理器10和11发挥作用。NEON和VFP指令按字母顺序分组到一起,因为他们所有助记符的首字母都有V

大多数指令都可以在指令编码中指定的不同数据类型上运行。 软件通过在指令助记符后加后缀来指定数据的字长。操作数据元素的数量由指定的寄存器长度决定。例如,VADD.I16 q0, q1, q2表示对存储于128位Q寄存器中的16位整型数据的操作。这意味着这个操作可以达到8通道16位数据的并行计算。

有些指令的输入和输出寄存器可能有不同的大小。例如,VMULL.S16 Q0, D2, D3并行地乘以四个16位通道,并在128位目标向量中产生四个32位的结果,如下图(译者注)

为了提高代码密度和性能,NEON指令集包括结构化加载和存储指令,可以从矢量寄存器的单个或多个通道加载数据,也可以将单个或多个数据存储到矢量寄存器的通道。NEON还包括在多个向量寄存器和存储器之间传输完整数据结构的指令,包括交叉(interleaving)和解交叉(de-interleaving)。

NEON开发

为了更好的使用新特性,你必须使用最新版本的编译工具。GNU 工具和 RealView Compilation Tools(RVCT) 的最新版本都支持NEON指令集。

汇编器

最直接的使用NEON单元的方式就是写汇编代码。NEON 指令集的一致性设计使编写汇编代码比您预期的要简单。

GNU和RVCT汇编器使用同样的指令格式,但是语法略有不同。不同的地方包括:

  • 汇编器指令
  • 标签的格式(label)
  • 注释的符号

例1.1 展示了一个使用GNU汇编器执行NEON指令的汇编函数。例1.2 展示了RVCT格式的相同的代码。这两个示例都使用硬件浮点链接,这意味着软件在 NEON 寄存器中传递和返回参数。

Example 1.1

 .text.arm.global double_elements
double_elements:vadd.i32 q0,q0,q0bx           lr.end

要使用GNU汇编器汇编示例 1.1 中的代码,请将 -mfpu=neon 添加到汇编器命令行。这一指定使得NEON指令被允许使用。例如:
arm-none-linux-gnueabi-as -mfpu=neon asm.s

Example 1.2

    AREA RO, CODE, READONLYARMEXPORT double_elements
double_elementsVADD.I32 Q0, Q0, Q0BX       LREND

要使用RVCT汇编示例1.2中的代码,请一定要指定一个目标处理器以支持NEON指令集。例如:
armasm --cpu=Cortex-A8 asm.s

Intrinsics

(Intrinsics不太好翻译,我理解就是内置函数,在C/C++高级语言规范里提供了一套内置函数来支持SIMD向量化)

Intrinsics(内置函数和数据类型)提供了类似与内联汇编一样的功能,并提供了像类型检查和自动寄存器分配这样的附加功能。一个Intrinsic函数在C或C++中以函数调用的形式出现,然后再编译阶段被替换为一系列低级别的指令。这意味着你可以在高级别语言中表达低级别架构行为。

除了提供给程序员直接访问指令(和高级别语言没有很好的映射起来)的能力之外,使用 Intrinsic 函数意味着编译器可以优化操作以提高性能。 使用 Intrinsic 意味着开发人员不必考虑寄存器分配和互锁问题,因为编译器会处理这些问题。

GCC和RVCT支持相同的NEON Intrinsic 语法,使得C/C++代码在不同工具链之间可移植。为了添加对NEON Intrinsic 的支持,需要引入头文件arm_neon.h。例1.3 在C中使用 Intrinsic 替代汇编指令,实现了和汇编示例同样的功能。

Example 1.3

#include <arm_neon.h>uint32x4_t double_elements(uint32x4_t input)
{return (vaddq_u32(input,input));
}

编译示例

尽管GNU和RVCT开发工具支持同样语法的NEON Intrinsics,但是这两个工具链的命令行语法不尽相同。

使用GCC编译NEON Intrinsics

为了在GCC中使用NEON Intrinsics,你必须指定-mfpu=non编译选项。
arm-none-linux-gnueabi-gcc -mfpu=neon intrinsic.c

根据您的工具链,您可能还必须添加-mfloat-abi=softfp 以向编译器指示必须在通用寄存器中传递 NEON 变量。
支持的Intrinsics完整列表可以在 http://gcc.gnu.org/onlinedocs/gcc/ARM-NEON-Intrinsics.html 中找到。

使用RVCT编译NEON Intrinsics

当你在编译选项中指定一个支持NEON指令的目标处理器,那么RVCT就可以接受NEON Intrinsics。例如:
armcc --cpu=Cortex-A9 intrinsic.c

你可以查阅 RVCT 编译指南(通过 http://infocenter.arm.com)来获取更多关于RVCT支持的Intrinsic函数和向量数据类型的信息。

自动向量化

编译器也可以针对你的C/C++源代码提供自动向量化的能力。这无需编写汇编代码或使用 Intrinsic 函数即可获得较高的NEON性能。这允许您的源代码在不同工具和目标平台之间保持可移植性。

因为C语言并没有指定并行行为,所以你需要给编译器额外的提示,告诉编译器那块是安全且最佳的。您可以在不影响源代码在不同平台或工具链之间的可移植性的情况下做到这一点。

例1.4 展示了一个编译器可以安全且最佳地向量化的小函数。编译器能够进行向量化的原因是程序员使用了 __restrict 关键字来保证指针 pa 和 pb 不会寻址重叠的内存区域。

void add_ints(int * __restrict pa, int * __restrict pb, unsigned int n, int x)
{unsigned int i;for(i = 0; i < (n & ~3); i++)pa[i] = pb[i] + x;
}

编译示例
尽管GNU和RVCT开发工具支持同样的源代码语法,但是命令行语法还是不尽相同。

使用GCC自动向量化
为了开启自动向量化,你必须添加-mfpu=neon-ftree-vectorize选项。例如:

arm-none-linux-gnueabi-gcc -mfpu=neon -ftree-vectorize -c vectorized.c

根据您的工具链,您可能还必须添加-mfloat-abi=softfp 以向编译器指示必须在通用寄存器中传递 NEON 变量。
您可以通过将 -ftree-vectorizer-verbose=1 添加到命令行来请求更详细的编译器输出。 这使得编译器输出以下信息:

  • 编译器向量化的代码
  • 编译器不能向量化的代码,并给出不能向量化的原因提示

你可以利用这些信息将代码修改为编译器可向量化的形式。某些版本的 GCC 支持大于 1 的 verbose 参数值,从而提供有关向量化的更多详细信息。

使用RVCT自动向量化

为了开启自动向量化,你必须指定一个实现了NEON技术的目标处理器,开启编译优化选项-O2或更高级别优化选项,以及添加编译选项-Otime--vectorize。例如:

armcc --cpu=Cortex-A9 -O3 -Otime --vectorize -c vectorized.c

注意
仅指定 --vectorize 是不行的,您还要指定 -Otime 和 -O2 或 -O3 优化级别才会启用自动矢量化。

因为浮点值的并行累积会降低通过对输入数据进行排序获得的精度,所以除非您在命令行上指定 --fpmode=fast,否则这些会被禁用。
您可以通过在命令行中添加 --remarks 来请求更详细的编译器输出。 这提供了有关编译的许多方面的附加信息。 对于 NEON 矢量化,这包括:

  • 编译器向量化的代码
  • 编译器不能向量化的代码,并给出不能向量化的原因提示

这些信息可用于将代码修改为编译器能够向量化的格式。

使用 NEON 优化库

在你的系统使用NEON技术最简单的方式是通过使用已经经过NEON优化过的库。

OpenMAX

OpenMAX 是由 Khronos Group 创建和分发的免版税跨平台 API 标准。ARM 创建了 OpenMAX 开发层 (DL) 的 ARMv7 NEON 优化实现。你可以在http://www.arm.com下载这个库。

例 1.5 通过调用 OpenMAX 函数 omxSP_DotProd_S16() 计算两个有符号 16 位整数向量中的值的点积。当使用 ARMv7 优化的 OpenMAX DL 库时,此函数是使用 NEON 矢量运算实现的。

#include <omxSP.h>OMX_S16 source1[] = {42, 23, 983, 7456, 124, 11111, 4554, 10002};
OMX_S16 source2[] = {242, 423, 9832, 746, 1124, 1411, 2254, 1298};OMX_S32 source_dotproduct(void)
{OMX_INT len = sizeof(source1)/sizeof(OMX_S16);return omxSP_DotProd_S16(source1, source2, len);}

ARM SIMD NEON 简介 (翻译自 Introducing NEON Development Article)相关推荐

  1. ARM SIMD 指令集:NEON 简介

    ARM SIMD 指令集:NEON 简介 一.NEON 简介 1.1.NEON 简介 1.2.NEON 使用方式 1.3.编译器自动向量化的编译选项 1.3.1 Arm Compiler 中使能自动向 ...

  2. RISC、CISC、 SIMD、FPU、MMX、SSE、SSEX、AVX、3D Now以及DSP、ARM的Neon简介

    CPU的指令集从主流的体系结构上分为精简指令集和复杂指令集,而在普通的计算机处理器基本上是使用的复杂指令集.在计算机早期的发展过程中,CPU中的指令集是没有划分类型的,而是都将各种程序需要相配合的指令 ...

  3. ARM Cortex系列(A8/A9/A15/A7) NEON多媒体处理SIMD引擎优化

    出处: http://houh-1984.blog.163.com/blog/static/31127834201211275111378/ Cortex-A9的NEON多媒体处理器是基于ARMv7的 ...

  4. ARM Neon 简介

    "ARM Advanced SIMD",nick-named "NEON", it provides:     (1).A set of interesting ...

  5. ARM平台处理器简介-ARMv7

    初次接触到ARM的时候,我直接被众多的处理器版本.系列搞晕了,查了好多资料才理清.现在在这里总结一下,希望能帮到别人. 1.总体情况 先从ARM的wiki上抄个表过来: Architecture Fa ...

  6. android ndk neon,Android NDK开发之 NEON使用介绍

    首先找到了要在C源代码中只用NEON库需要的头文件 arm_neon.h. #include //在代码中先添加了这行语句,然后执行ndk-build 却提示了错误 //提示要增加什么标志,自己在 L ...

  7. PhoneME简介(翻译)

    PhoneME简介(翻译) 作者:陈跃峰 出自: http://blog.csdn.net/mailbomb phoneME Feature software是一个优化了的Java ME架构.它的核心 ...

  8. neon浮点运算_Linux下VFP NEON浮点编译

    http://blog.csdn.net/liujia2100/article/details/27236477 NEON:SIMD(Single Instruction Multiple Data ...

  9. 关于ARM的一些简介

    计算机体系结构分类 两种典型的结构:  冯·诺依曼结构  哈佛体系结构 冯·诺依曼结构 冯·诺依曼机:将数据和指令都存储在存储器中的计算机.  计算系统由一个中央处理单元(CPU)和一个存储器组成.存 ...

最新文章

  1. 学习RGB配色,灰度图
  2. will not add file alias already exists in index(git上传代码出错)
  3. 第21届国际C语言混乱代码大赛获奖作品
  4. python hist 参数_关于python中plthist参数的使用详解
  5. 完全优化MySQL数据库性能的八大巧方法
  6. 管理好你的愿望,人生将另一个样
  7. springsession分布式登录被覆盖_拉勾 分布式 学习小结
  8. TreeView控件的基本使用 界面篇 winform
  9. 推荐]招商就象谈恋爱
  10. JEESZ分布式框架--单点登录集成方案
  11. SpringMVC 参数绑定注解解析
  12. Codeproject文章翻译
  13. 0基础参加数学建模,最大程度冲击奖项
  14. java三三剩二五五剩三,大年三十彩灯悬,彩灯齐明光灿灿,三三数时能数尽,五五数时剩一盏,七七数时刚刚好,八八数时还缺三,...
  15. html及css中页面总宽度的代码,css 宽度(CSS width)
  16. Java爆笑梗,jvav是什么鬼!盘点那些迷你小学生中那些笑死人的梗
  17. 操作性条件作用和经典性条件作用中,刺激分化和泛化的区别是?|小白心理-312/347考研答疑
  18. JGG:肠道菌群与COVID-19重症风险密切关联
  19. Android开发:如何实现收发短信
  20. rem适配布局 Less基础 插件 cssrem

热门文章

  1. 【电路设计】AD中通过开窗来绘制不规则的焊盘
  2. 瓜子二手车逃离行业“不可能三角”?
  3. 职业自我认知的测试软件,职业生涯规划自我认知测试.docx
  4. 越千年,是谁负了这情长
  5. 《白日梦想家》影评笔记
  6. c语言编程的扑克牌游戏,扑克牌加减乘除游戏
  7. AndroidStudio小松鼠版本添加jitpack.io位置
  8. English--vowels_单元音
  9. SpringCloud Alibaba Senta处理分布式事务
  10. 周志明架构课--03.SOA时代:成功理论与失败实践