(NEON实例一)ARM处理器NEON编程及优化技巧——数据加载和存储

ARM 286浏览

转载地址:http://blog.csdn.net/hw5226349/article/details/45063439

ARM的NEON协处理器技术是一个64/128-bit的混合SIMD架构,用于加速包括视频编码解码、音频解码编码、3D图像、语音和图像等多媒体和信号处理应用。本文主要介绍如何使用NEON的汇编程序来写SIMD的代码,包括如何开始NEON的开发,如何高效的利用NEON。首先会关注内存操作,即如何变更指令来灵活有效的加载和存储数据。接下来是由于SIMD指令的应用而导致剩下的若干个单元的处理,最后是一个例子来说明用NEON来进行SIMD优化。

SIMD的例子

首先看一个实例,24-bit的RGB图像,像素在内存里的组织方式是R, G, B, R, G, B...,如果你想做一个简单的图像处理,比如把R和B通道互换,你该如何高效的使用NEON协处理器呢?首先从存储空间线性加载RGB数据到D寄存器,然后交换R和B数据。 但是这种线性加载的数据进行R和B通道的数据交换非常麻烦,需要首先掩码mask,然后移位并合并掩码数据。这种复杂的运算显然并不高效。


图1. 线性加载RGB数据

NEON提供了各种结构的加载和存储指令来处理这种情况,这些指令能把数据从存储区加载的同时还能把这些值分开存储到不同的寄存器中,如下图2所示,你可以使用VLD3加载来把RGB数据分开存储。


图2. 使用结构化的加载指令加载RGB数据

然后使用VLD3分开加载的数据就能方便的使用指令(VSWP d0, d2)来进行R和B通道的交换了,然后把结果再写入内存,当然也要使用interleave交织模式的存储,即VST3存储指令。


图3. 交换寄存器d0和d2然后存储

结构化加载和存储语法和具体指令

NEON结构化加载会读取内存内容到64-bit的NEON寄存器,使用可选的de-interleave选项,同样加载指令也可以采用这种reinterleave的方式把寄存器的内容写到内存空间。


图4. NEON的结构化加载和存储

下面介绍NEON存储和加载的结构化方式,语法包括如下5个部分:


图5. NEON的结构化加载和存储语法

  • 加载VLD或者存储VST指令助记符:instruction mnemonic(指令助记符)
  • 一个表示interleave模式的数字,表示每个结构体元素间的间隔:interleave pattern(交织模式) 
  • 表示每次访问单元的位宽比特数,即结构体内元素类型:element type(元素类型)
  • 读写的64-bit的NEON寄存器集合,最多可以列出4个寄存器,取决于interleave模式:NEON registers
  • 表示内存访问地址的ARM寄存器,该地址可以在每次访问时更新: ARM address register

交织模式:Interleave Pattern

加载和存储指令可以用从1到4个相同大小的元素的交织结构体,这些元素可以是NEON指令通常支持的8,16或者32比特。

  • VLD1是最简单的形式,从内存加载1~4个寄存器的数据,没有deinterleave,即线性加载;
  • VLD2加载2或者4个寄存器的数据,解交织奇偶元素到各自的寄存器,这样很容易的把交织的立体声音频数据分解为左右声道的数据;
  • VLD3加载3个寄存器的数据,很方便的把RGB的数据分为R、G、B通道;
  • VLD4加载4个寄存器的数据,解交织,用于分解ARGB图像数据;

存储和加载类似,只是把寄存器的数据interleave然后写到内存。

元素类型 Element Types

加载和存储interleave的数据的基本元素可以为8,16或者32比特的数据。比如NEON指令VLD2.16将加载4个16-bit元素到第一个寄存器,然后4个16-bit元素到第二个寄存器,把临近的奇偶对分开保存到每个寄存器。


图6. 加载并解交织16-bit的数据

把元素大小变成32-bits还是加载相同大小的数据,但是只有2个元素来构成一个向量,同样分成奇偶元素部分。


图6. 加载并解交织32-bit的数据

元素大小还会影响大小端的处理。如果你指定了正确的加载和存储指令的元素大小,从存储空间读取和存储的字节都会按照正确的次序排列,因而相同的代码会在大端和小端系统里适用。最后元素大小还会影响指针的数据对齐,把数据对齐到元素大小的边界能带来更好的性能,当然这也是一般的操作系统OS要求的。如,加载32位元素数据,通常要把第一个元素的地址对齐到32位边界。

单个或者多个元素 Single or Multiple Elements

除了加载多个元素,结构读取还能从内存用deinterleave的模式读取一个元素到NEON寄存器的多个通道或者一个通道而保存其他通道不变。


图7. 加载并解交织到所有的通道

加载到单一通道对于从分散的内存空间的数据读取来构建一个向量非常有用。存储数据到内存也是一样,也可以采用通道的模式进行存储。


图8. 加载并解交织到单一的通道

寻址模式Addressing

NEON的加载和存储指令支持3种格式的寻址模式:

  • 寄存器[Rn {,:align}]:数据将会从指定寄存器的地址加载或者存储;
  • 带自动加减地址更新的寄存器 [Rn {,:align}]!:在数据从指定寄存器的地址加载或者存储后会更新地址,更新的地址大小等于读取或者存储的数据元素大小;
  • 带后索引的寄存器 [Rn {,:align}], Rm:存储空间访问后,会根据制定的寄存器Rm来更新地址,当需要读取或者存储一组具有固定间隔的数据时非常有用。

其他的加载和存储

NEON还支持以下的数据加载和存储:

  • VLDR和VSTR来加载和存储64-bit数值到一个单一的寄存器;
  • VLDM和VSTM来从堆栈加载或者存储多个64-bit数值到多个寄存器;

更多的关于支持的加载和存储运算可以参考ARM的参考手册ARM 《Architecture Reference Manual》. 详细的指令周期信息可以参考每个单独的指令《Technical Reference Manual for each core》.