前言

ARM NEON 可以提升音视频,图像,计算机视觉等计算密集型程序的性能,在上一篇大前端CPU优化技术--NEON技术的介绍中,我们知道一些编译器可以将 C/C++ 代码自动转换为 NEON 指令,这种技术称为自动向量化,我们总是想要追求最极致的性能,想要极力的榨干CPU的性能,而自动向量化技术受限于编译器的规则和环境限制很难达到最理想的收益。所以想要有更好的性能还是需要手工编写 NEON 汇编代码或者用内联函数,但熟练掌握 NEON 指令是实现理想性能的基础。

本文接下来会详细地介绍 Armv7 和 Armv8 架构下 NEON 向量寄存器、NEON 汇编指令格式、NEON Intrinsics 指令格式等内容,感谢耐心阅读。

NEON指令格式

ARM 主流架构

Armv7-A 和 Armv8-A 的关系如下图所示

Arm v8-A是一个非常重要的架构变化,它支持64位执行模式 “AArch64” ,并且带来了全新的64位指令集 “A64” 。同时,为了兼容Arm v7-A (32位架构)指令集,也引入了 “AArch32” 的概念。

  • AArch64 是 64 位执行状态,支持 A64 指令集。
  • AArch32 是 32 位执行状态,支持 T32 和 A32 指令集,同时 AArch32 与 AArch64 中一些的功能保持一致,而且 AArch32 兼容 Armv7-A。

ARM v7 的NEON是可选的,而在ARM v8上是默认支持的,ARM v8 支持浮点类型的除法向量操作,这是ARM v7所没有的。另外AArch64还支持double类型的操作。

Armv9-A 是 arm 当前最新的指令架构,Armv9-A 除了向前兼容 Armv8-A,在性能计算上有了很大的提升,主要表现在安全、AI 以及改进矢量扩展(SVE2)和 DSP 能力。

AArch64 向量寄存器

AArch64 有 32 个 128bit 的向量寄存器,这些寄存器又可以划分为:

  • 32 个 128bit 的 V 寄存器,V0~V31。
  • 32 个 64bit 的 D 寄存器,D0~D31。
  • 32 个 32bit 的 S 寄存器,S0~S31。

每种类型寄存器的映射关系如下:

AArchh32 / ARMV7向量寄存器

AArch32/Armv7 有 16 个 128bit 的向量寄存器,这些寄存器又可以划分为:

  • 16 个128bit 的 Q 寄存器,Q0~Q15。
  • 32 个 64bit 的 D 寄存器,D0~D31。
  • 32 个 32bit 的 S 寄存器,S0~S31。

每种类型寄存器的映射关系如下:

大前端CPU优化技术--NEON技术也有介绍NEON的寄存器,每个128-bit向量寄存器可以当做:

包含 2 个 64-bit 元素的向量寄存器来用,表达形式是 vn.2d;
包含 4 个 32-bit 元素的向量寄存器来用,表达形式是 vn.4s;
包含 8 个 16-bit 元素的向量寄存器来用,表达形式是 vn.8h;
包含 16 个 8-bit 元素的向量寄存器来用,表达形式是 vn.16b;
或每个向量寄存器也可以只用低 64-bit位:

1 个 64-bit 元素的向量寄存器来用,表达形式是 vn.1d;
2 个 32-bit 元素的向量寄存器来用,表达形式是 vn.2s;
4 个 16-bit 元素的向量寄存器来用,表达形式是 vn.4h;
8 个 8-bit 元素的向量寄存器来用,表达形式是 vn.8b;

NEON指令格式

AArch64 与AArch32 / Armv7-A 的 NEON 汇编指令除了种类上存在差异,格式上也存在很大差异。

指令中有一些通用的书写格式, 含义如下:

  • {}, 表示可选项
  • <>, 表示必选项

AArch64 NEON指令格式通用描述如下:

{<prefix>}<op>{<suffix>}Vd.<T>,Vn.<T>,Vm.<T>

指令解读如下:

<prefix> 前缀:

  • 如S/U/F/P 分别表示有符号整数/无符号整数/浮点数/布尔数据类型
  • Q:表示饱和(Saturating)计算。
  • R:表示舍入(Rounding)计算, Rounding 操作等价于加上 0.5 之后再截断。
  • H:表示折半(Halving)计算。
  • D:表示翻倍(Doubling)算。

<op> 操作符。例如ADD,AND等。

<suffix> 后缀,通常是有以下几种:

  • P:将向量按对操作,例如ADDP
  • V:跨所有的数据通道操作,例如FMAXV
  • H:表示结果只取每个通道的高半部分(High)
  • L/N/W/L2/N2/W2:表示数据长度的变化
  • 2:在宽指令/窄指令中操作数据的高位部分。例如ADDHN2,SADDL2。

ADDHN2:两个128位矢量相加,得到64位矢量结果,并将结果存到NEON寄存器的高64位部分。
SADDL2: 两个NEON寄存器的高64位部分相加,得到128-位结果。

<T>  数据类型,通常是8B/16B/4H/8H/2S/4S/2D等。B代表8bit数据类型;H代表16位数据宽度;S代表32位数据宽度,可以是32位整数或单精度浮点;D代表64位数据宽度,可以是64位整数或双精度浮点。

UADDLP V0.4S, V2.2D
// 指令: 将 V2 向量相邻相邻元素进行两两和后,并将结果保存在V0的低64位上
//
// U -- 表示无符号操作
// ADD -- 表示加操作
// P -- 将向量按对操作
// L -- 表示将结果保存到输出向量的低 64bit
// V2.2D -- 表示输入向量寄存器,长度为 64bit,一共两个通道,每个通道 64bit
// V0.4S -- 表示输出向量寄存器,长度为 128bit,一共四个通道,每个通道 32bit

 AArch32 / Armv7汇编指令格式

AArch32/Armv7 NEON指令格式通用描述如下:

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

指令解读如下:

V 固定格式,AArch32 / Armv7 以"V"开头,表示vector向量指令,也就是NEON指令

<mod> 该修饰字可以表示为以下类型:

  • Q, 表示饱和(Saturating)计算。
  • R, 表示舍入(Rounding)计算,Rounding 操作等价于加上 0.5 之后再截断。
  • H, 表示折半(Halving)计算。
  • D, 表示翻倍(Doubling)计算。

<op> 表示 操作运算符 如ADD, SUB, MUL

<shape> - 数据长度,Long (L), Wide (W), Narrow (N).

<.dt> - 数据类型,如 s8, u8, f32。默认为第二个操作数的数据类型。如果第二个操作数不存在,为第一个操作数类型,仍不存在为结果操作数类型。

<dest> - 输出.

<src1> ,<src2>- 输入操作数

注: {} 表示可选的参数。

VADD.S16 D0, D1, D2  16位加法// 指令语句作用:
//              64bit 向量 D1 和 D3 中每个元素相加赋值给向量 D0
//
// 指令格式说明:
//            ADD -- 表示加法操作
//           .S16 -- 表示操作元素的数据类型为有符号 16bit
//             D0 -- 表示输出向量寄存器,长度为 64bit
//             D1 -- 表示输入向量寄存器,长度为 64bit
//             D3 -- 表示输入向量寄存器,长度为 64bit

intrinsics指令格式

NEON Intrinsics 是一种更简单的编写 NEON 代码的方法,NEON Intrinsics 类似于 C 函数调用,在编译时由编译器替换为相应的汇编指令,使用时需要包含头文件arm_neon.hNEON intrinsics可以跨Arm v7-A/v8-A运行。

非数组向量格式

<基本类型>x<lane个数>_t      如int8x8_t

基本类型int8,int16,int32,int64,uint8等

lane个数表示并行处理的基本类型数据的个数(通道数)。

数组向量格式

<基本类型>x<lane个数>x<向量个数>_t    如 uint8x8x3_t

对于多个向量的类型实际上是结构体

 typedef struct {uint8x8_t val[3];} uint8x8x3_t;

函数格式

v<mod><op><shape>[suffix]_<type>

<mod>

q:表示饱和计算,

a加b的结果做饱和计算
int8x8_t vqadd_s8(int8x8_t a, int8x8_t b);

h:表示折半计算,

a减b的结果右移一位
int8x8_t vhsub_s8(int8x8_t a, int8x8_t b);

d:表示加倍计算,

 a乘b的结果扩大一倍, 最后做饱和操作int32x4_t vqdmull_s16(int16x4_t a, int16x4_t b);

r:表示舍入计算,例如:

将a与b的和减半,同时做rounding 操作, 每个通道可以表达为: (ai + bi + 1) >> 1
int8x8_t vrhadd_s8(int8x8_t a, int8x8_t b);

p:表示pairwise计算。例如:

将a, b向量的相邻数据进行两两和操作
int8x8_t vpadd_s8(int8x8_t a, int8x8_t b);

<指令名>表示具体操作,比如 add,sub。

<shape>

l:long,表示长指令,输出数据的基本类型位数是输入的2倍,

 uint16x8_t vaddl_u8(uint8x8_t a, uint8x8_t b);

n:narrow,表示窄指令,输出数据的基本类型位数是输入的一半,

uint32x2_t vmovn_u64(uint64x2_t a);

w:wide,第一个输入向量和输出向量类型一样,且是第二个输入向量元素长度的2倍,

uint16x8_t vsubw_u8(uint16x8_t a, uint8x8_t b);

_high:AArch64专用,而且和 l/n 配合使用。当使用 l(Long) 时,表示输入向量只有高 64bit 有效;当使用 n(Narrow) 时,表示输出只有高 64bit 有效。

 // a 和 b 只有高 64bit 参与运算
int16x8_t vsubl_high_s8(int8x16_t a, int8x16_t b);

_n:表示有标量参与向量计算,

// 向量 a 中的每个元素右移 n 位
int8x8_t vshr_n_s8(int8x8_t a, const int n);

_lane: 指定向量中某个通道参与向量计算,

// 取向量 v 中下标为 lane 的元素与向量 a 做乘法计算
int16x4_t vmul_lane_s16(int16x4_t a, int16x4_t v, const int lane);

[suffix]

后缀如果没有,表示64位并行;如果后缀是q,表示128位并行。

<type>

数据基本类型简写:s8,s16,s32,s64,u8,u16,u32,u64,f16,f32,f64

例如:

vadd_u16:两个uint16x4相加为一个uint16x4vaddq_u16:两个uint16x8相加为一个uint16x8

总结

本文主要介绍了 NEON 指令相关的知识,首先我们了解了 arm 在不同架构下指令集的分类,尤其是NEON相关的特点和区别,然后我们介绍了向量寄存器,了解了寄存器的种类和数量。最后我们又介绍了不同指令集下的NEON指令格式,这对于后面的汇编编写及性能调优是非常重要的。

工欲善其事,必先利其器,我们只有掌握了基本的指令和寄存器,才能对后续的汇编编程有更好的理解和更多的调优手段,后续我们会继续深入解析NEON指令。希望大家能在本文中有所收获。

大前端CPU优化技术--NEON指令介绍相关推荐

  1. 大前端CPU优化技术--NEON intrinsics进阶

    前言 今天我们继续介绍NEON intrinsics的指令知识,上篇大前端CPU优化技术--NEON intrinsics开篇中已经介绍了部分指令的作用.本篇文章除了介绍指令还会附上场景示例,方便大家 ...

  2. 大前端CPU优化技术--NEON技术

    前言 在上一篇中我们讲了SIMD技术的基础和前世今生,可以结合上一篇文章一起看大前端CPU优化技术--SIMD技术.今天我们全局性地讲解下NEON技术​. 目前主流的移动设备以ARM v7和v8版本架 ...

  3. 大前端CPU优化技术--NEON自动向量化

    前言 ARM NEON技术是一种高级的单指令多数据的架构扩展的实现.它是一种64位和128位混合的SIMD技术,主要应用场景是音视频处理,图像视觉计算,信号处理应用等需要密集计算的场景. NEON技术 ...

  4. 大前端CPU优化技术--NEON编程优化技巧

    前言 在前面的文章中我们介绍了NEON的基础,NEON技术的全景,指令及NEON intrinsic指令,相信大家能通过前面的学习写一些简单的NEON程序.但要想写好一个性能高的NEON程序,远不止你 ...

  5. 浅谈大前端的代表技术及其影响,值得我们思考

    到底哪些是大前端的代表技术?从业务上来说,我认为终端 开发.网关设计.接口设计.桌面端的 工程化都可以算是大前端的业务范畴. 具体的技术,则是基于 HTML5.NodeJS 的通用技术,以及各平台的专 ...

  6. 前端SEO优化技术汇总

    一.title.alt.h1 title: 网站头部标签<head>下的title,网站名称 备注:这里为什么不说标签中的title属性,,虽然鼠标上移可以显示图片名字,但是它跟SEO没一 ...

  7. 前端18个月难度翻番?来这里把握大前端技术本质进展丨稀土开发者大会

    图片来源:pexels.com "别更新了,学不动了"向来是前端开发群体的切肤之痛: React 还没学明白,Vue 就出来了: Vue 2.0 还没上手,3.0 就发布了: No ...

  8. 林云会研究院参加“创新大前端 研发新思路”线下技术沙龙活动

    2022年9月25日,由凡泰极客主办.优姆领克协办的"创新大前端.研发新思路"线下技术沙龙活动在南山科技园桑达科技大厦蓝马咖啡成功举办,林云会数字经济研究院应邀出席活动,与业内资深 ...

  9. 大数据和云计算技术周报(第88期)

    导语 值此佳日,祝社区的同学们春节快乐,阖家幸福! 本期会给大家奉献上精彩的:AI.知识图谱.flink.Hbase.系统设计.DL.Spark.Redis.全是干货,希望大家喜欢! 节假日不打烊,是 ...

最新文章

  1. python下载文件到指定文件夹-python实现指定文件夹下的指定文件移动到指定位置...
  2. DataV 支持 token 验证啦!
  3. ijkplayer-android框架详解
  4. python抢票_50 个加速包都抢不到车票,还不如这个 Python 抢票神器!
  5. python微服务监控_如何用zabbix监控微服务
  6. anaconda下载太慢怎么办_BBTIME我爱上了 Anaconda
  7. 别人家的防疫实时监控大屏是怎么做的?方法和技巧都在这里了
  8. 解读年度数据库PostgreSQL:如何处理并发控制(一)
  9. matlab 曲线拟合--视频编码中PSNR计算及码率计算
  10. python des加密文件_python DES3 加密解密
  11. c51语言自定义头文件,C51语言头文件包括的内容有
  12. python底层原理讲解_python底层原理
  13. oracle 启动报错03113,oracle数据库无法启动,总报ora-03113错误
  14. 程序员记录biji的工具_程序员专用笔记 Quiver
  15. 有关private T
  16. 21秋信源编码技术作业(1)——使用Audacity软件绘制清浊音频谱图并进行分析
  17. 重置海康威视DS-2TD2617B-6/PA测温摄像头
  18. 运用limma对基因进行差异分析
  19. Java池化技术你了解多少?
  20. 确定sw1开关信号输入端口_老电工教大家变频器,怎么接入浮球液位调节信号

热门文章

  1. 保持应用后台,熄屏状态下继续运行
  2. requests.get
  3. 在word修改模式下如何进行修改
  4. springboot下载依赖包
  5. 导入数据库显示服务器发生意外,mysql 数据库无法启动(Ignoring the redo log due to missing M...
  6. 2023年技术分析|血氧仪芯片方案
  7. HDOJ 5100 Chessboard 构造
  8. CentOS7 安装 chrome
  9. mac 版 Goland 使用教程一
  10. ef连接mysql报root没有权限_EF下使用自定义的connectionString避免数据库密码泄露