整理一下以前使用过的ARM NEON.
1. NEON简介
NEON是ARM处理器提供的高级SIMD(Single Instruction, Multiple Data)扩展, 用于多媒体/深度学习框架等方面的硬件加速.
查看ARM处理器是否支持NEON
1 | cat /proc/cpuinfo |
ARMv7处理器会显示neon
ARMv8处理器会显示asimd
如下是Cortex-A57处理器显示的信息
1 | processor : 0 |
2. NEON寄存器
NEON寄存器可用于:
- 存储浮点数操作数(浮点数指令)
- 存储标量和矢量操作数(NEON指令)
ARMv7-A和ARMv8-A的aarch32, 有16个128bit的NEON寄存器(q0~q15), 对应32个d0~d31, q0对应d0和d1, q1对应d2和d3, 依次类推.
ARMv8-A的aarch64, 有32个128bit的NEON寄存器(v0~v31), 对应32个d0~d31, v0对应d0, v1对应d1, 依次类推.
下面从三个操作数角度认识NEON寄存器(ARMv8-A的aarch64)
2.1 浮点数
浮点器寄存器分3类
- 32个半精度寄存器: h0~h31
- 32个单精度寄存器: s0~s31
- 32个双精度寄存器: d0~d31
浮点数寄存器可以直接用s0, d0等访问.
2.2 标量
标量就是矢量中的单个值, 用下标来访问, 如v0.s[0]
2.3 矢量
NEON寄存器用于矢量时, 将16字节的NEON寄存器平均划分成若干个lane, 每个lane有相同类型/大小的element. 如v0.8h, 表示分成8个lane, 每个lane是两字节大小.
这类似于车道, 车道越多, 单位时间内通行的车辆越多.
3. NEON编程
NEON编程有如下几种方式
- NEON intrinsic
- NEON asm
3.1 NEON intrinsic
NEON intrinsic是ARM提供的一套NEON APIs, 介于ARM汇编和C/C++之间的一套接口. 使用它可以快速地使用NEON进行优化, 编译器负责具体寄存器分配等底层操作, 可移植性较好. 一般推荐使用此种方法来实现相关优化.
头文件
1
3.2 NEON asm
NEON汇编需要开发者手动编写NEON指令, 完成指定的功能, 开发难度较大. 性能一般较优, 但可移植性差, 不同处理器可能使用的指令不同.
3.3 demo
以dotProduct为例, 对比下NEON的带来的性能提升.
3.3.1 测试环境
- Linux ubuntu18arm64 4.15.0-76-generic #86-Ubuntu SMP Fri Jan 17 17:25:58 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
- gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)
- C++11
- CMake Release build版本(默认打开-O3选项)
3.3.2 测试源码
CPP版本
1 | float CPP::dotProduct(const std::vector<float> &v1, |
对应ARM汇编如下
1 | 00000000000030e0 <_ZN3CPP10dotProductERKSt6vectorIfSaIfEES4_>: |
NEON版本(intrinsic)
1 |
|
对应ARM汇编如下
1 | 0000000000003260 <_ZN13NEONIntrinsic10dotProductERKSt6vectorIfSaIfEES4_>: |
相关的NEON指令代码如下
1 | ... ... |
3.3.3 测试结果
在Release版本下, NEON版本相对于CPP版本, 性能提升大约12%左右
1 | size = 256 |