嵌入式程序优化(1)-内嵌neon汇编

1. neon介绍

neon指令集arm 平台的 SIMD 指令集,也即单指令多数据指令集,如名字所说,一条只能可以同时处理多个数据,这里常常也使用另外一个名词来说 向量化编程。向量化编程在音视频处理领域中极为常见,随着人工智能深度学习等技术在嵌入式平台上的应用,neon指令集也可以被使用来优化某些后处理函数。本文将通过说明加代码例子讲解的方式尽量来阐述笔者的理解

PS:本文默认读者们已经熟悉了arm内嵌汇编语法

2. neon指令基础

本节将着重讲解一下 neon指令集 的基础知识,从而让读者更好地理解如何使用 neon指令

2.1 指令寄存器组

neon指令集 有专用的寄存器组,第一种是Q0-Q15一共16个,每个寄存器长度为128bit,第二种是D0-D31,一共32个,每个寄存器为64bit

neon寄存器有对照关系,可以理解为他们共同使用一块存储数据的区域,但该区域被分别映射为D寄存器和Q寄存器,下面的图是寄存器之间的对应关系

 
寄存器对应关系.png

neon指令集将每个寄存器视为均包含一个向量, 而该向量又包含 1、2、 4、 8 或 16 个大小和类型均相同的元素,也可以将各元素当作标量 加以访问。比如D0中有8个元素,每个元素是8bit,那么D0寄存器看成是有8个元素的向量

2.2 指令分类

neon指令 的操作对象为向量,向量有长有短,neon指令常用的有 64bit 的双字向量和 128bit 的四字向量其中,根据操作数的长度,可以将 neon指令 分为以下几种类型

  • 通用指令集(Normal instructions):普通指令可以对任何向量类型进行操作,并产生与操作数向量相同大小且通常与类型相同的结果向量。即向量与结果长度相同

  • 长整数指令集(Long instructions):长指令对 双字向量操作数 进行运算并产生 四字向量 结果。结果元素通常是操作数宽度的两倍,并且类型相同。即输入向量的长度为输出向量的一半(长指令使用附加在指令后的L来指定)。

  • 宽整数指令集(Wide instructions):宽指令对 双字向量操作数四字向量操作数 进行操作,产生 四字向量 结果 。结果元素和第一个操作数(四字向量操作数)的宽度是第二个操作数元素(双字向量操作数)的宽度的两倍。 即双字向量和四字向量计算后输出四字向量。宽指令在指令后附加了W。

  • 窄整数指令集(Narrow instructions):窄指令对 四字向量操作数 进行运算,并产生 双字向量 结果。结果元素通常是操作数元素宽度的一半。即四字向量和四字向量计算后输出双字向量。窄指令通过在指令后附加N来指定。

2.3 指令格式

V{<mod>}<op>{<shape>}{<cond>}{.<dt>}{<dest>}, src1, src2

  • mod:即模式,neon指令集有几种计算模式,如下 Q: 饱和计算,各个数据类型的饱和范围请查看表. H: 该指令将结果减半。 它实际上通过向右移一个位来完成此操作,例如VHADD,VHSUB。 D: 使用指令使结果加倍 R: 对截断的指令结果进行舍入,比如某一个指令的操作数为整形,但指令结果带有小数,则需要对小数 进行舍入

  • op:执行的操作,比如加法ADD, 减法SUB, 乘法MUL).

  • shape:用于指定长指令(L), 宽指令(W), 窄指令(N),指的就是2.2节中的指令分类

  • cond:条件码, neon条件码与普通的arm汇编条件码使用的是同一套条件码,但其含义不同,详情查看手册

  • datatype:操作的数据类型,比如U8,U16等

  • dest:目的寄存器

  • src1:源寄存器1

  • src2:源寄存器2

PS:带花括号的字段为可选字段

2.4 指令编译

在编译含有neon指令集的代码时,是指定使用的fpu和cpu,通常情况下我们使用arm-gcc需要加-ftree-vectorize-mfpu=neon-mcpu=your_chip_arch 来使能编译器使用neon指令集

neon指令集常常是在C语言中使用,除了使用汇编代码来编写外,我们还可以使用neon的开源库如Ne10等,也可以使用官方提供的neon内建函数

同时,gcc编译器 支持对一般的代码进行 neon优化,但需要满足以下条件

• 短而简单的循环

• 不使用break跳出循环.

• 循环次数为2的幂次.

• 循环次数为编译器支持的范围.

• 循环内部调试函数时,该函数需要是内联属性

• 使用数组索引而不是指针.

• 间接寻址无法向量化.

• 使用strict关键字告诉编译器指针不引用内存的重叠区域。

3. 例子说明

下面通过一个简单的 neon指令 的语句来了解一下具体的语法

 
指令示意.png

上面的图例应该展现得足够清楚

qd 是mod字段,表明该指令支持饱和和对结果进行双倍放大操作

mla是neon指令集操作,标志相乘并累加结果

l 表示的是指令的操作类型为长整型操作

s16 表示的是向量中的元素长度

dest 表示的是目标寄存器

src 表示的是源寄存器

关于 neon 的具体例子请参加笔者的github,地址为https://github.com/wipping/neon

因为该代码例程中有上千行,不便在文章中呈现,请读者们下载阅读,代码中做了相应的注释,当然还是要搭配neon指导手册 进行阅读。因为代码是根据手册中的内容进行编写

4. 参考资料

NEON简介及基本架构:http://zyddora.github.io/2016/02/28/neon_1/
ARM平台NEON指令的编译和优化https://blog.csdn.net/heli200482128/article/details/79303286
neon函数速查地址: https://developer.arm.com/architectures/instruction-sets/simd-isas/neon/intrinsics



作者:wipping的技术小栈
链接:https://www.jianshu.com/p/a57a348f35f5
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted on 2022-04-13 11:28  feng..liu  阅读(755)  评论(0编辑  收藏  举报