对NEON加速做一个简单的总结,遇到其他问题再记录整理,这里感谢几位博主的奉献。
先贴一个简单的例子,例子参考https://blog.csdn.net/dwyane12138/article/details/78697210。
#include<iostream>
#include<time.h>
#include <arm_neon.h>
using namespace std;
float sum_array(float* arr,int len);
float sum_array_neon(float* arr,int len);
double time_calc(struct timespec &tp_start, struct timespec &tp_end)
{
return (double)(tp_end.tv_nsec -tp_start.tv_nsec) * 0.000001 + (double)(tp_end.tv_sec - tp_start.tv_sec) * 1000.0;
}
int main()
{
long l=100000;
float array[l];
for(long i=0;i<l;i++)
{
array[i]=1.0/3.0;//i/10000;
}
struct timespec tp0,tp1,tp2,tp3;
float Sum,Sum1;
//calcuate without NEON
clock_gettime(CLOCK_MONOTONIC, &tp0);
//for(int round=1;round<=1000;round++)
//{
Sum=sum_array(array,l);
//cout<<Sum<<endl;
//}
clock_gettime(CLOCK_MONOTONIC, &tp1);
printf ("no neon result = %f\n",Sum);
printf ("no neon time=%lf ms \n\n\n", time_calc(tp0, tp1));
//calculate with NEON
clock_gettime(CLOCK_MONOTONIC, &tp2);
//for(int round=1;round<=1000;round++)
//{
Sum1=sum_array_neon(array,l);
//cout<<Sum<<endl;
//}
clock_gettime(CLOCK_MONOTONIC, &tp3);
printf ("neon result = %f\n",Sum1);
printf ("with neon time=%lf ms \n", time_calc(tp2, tp3));
return 0;
}
float sum_array(float* arr,int len)
{
if(NULL== arr||len<1)
{
cout<<"input error\n";
return 0;
}
float sum(0.0);
for(long i=0;i<len;++i)
{
sum+=*arr++;
}
return sum;
}
float sum_array_neon(float* arr,int len)
{
if(NULL== arr||len<1)
{
cout<<"input error\n";
return 0;
}
long dim4=len/4;
int left4=len%4;
float32x4_t sum_vec = vdupq_n_f32(0.0);
for(;dim4>0;dim4--,arr+=4)
{
float32x4_t data_vec = vld1q_f32(arr);
sum_vec = vaddq_f32(sum_vec,data_vec);
}
float sum = vgetq_lane_f32(sum_vec,0)+vgetq_lane_f32(sum_vec,1)+vgetq_lane_f32(sum_vec,2)+vgetq_lane_f32(sum_vec,3);
for(;left4>0;left4--,arr++)
{
sum+=(*arr);
}
return sum;
}
使用NEON首先引入头文件<arm_neon.h>,其次就可以根据NEON的编程指令进行程序的加速和优化,下面将上面用到的函数介绍如下,此处借用无眠栀的介绍:
上面用到的函数有:
float32x4_t vdupq_n_f32 (float32_t value)
将value复制4分存到返回的寄存器中
float32x4_t vld1q_f32 (float32_t const * ptr)
从数组中依次Load4个元素存到寄存器中
相应的 有void vst1q_f32 (float32_t * ptr, float32x4_t val)
将寄存器中的值写入数组中
float32x4_t vaddq_f32 (float32x4_t a, float32x4_t b)
返回两个寄存器对应元素之和 r = a+b
相应的 有float32x4_t vsubq_f32 (float32x4_t a, float32x4_t b)
返回两个寄存器对应元素之差 r = a-b
float32_t vgetq_lane_f32 (float32x4_t v, const int lane)
返回寄存器某一lane的值
其他常用的函数还有:
float32x4_t vmulq_f32 (float32x4_t a, float32x4_t b)
返回两个寄存器对应元素之积 r = a*b
float32x4_t vmlaq_f32 (float32x4_t a, float32x4_t b, float32x4_t c)
r = a +b*c
float32x4_t vextq_f32 (float32x4_t a, float32x4_t b, const int n)
拼接两个寄存器并返回从第n位开始的大小为4的寄存器 0<=n<=3
例如
a: 1 2 3 4
b: 5 6 7 8
vextq_f32(a,b,1) -> r: 2 3 4 5
vextq_f32(a,b,2) -> r: 3 4 5 6
vextq_f32(a,b,3) -> r: 4 5 6 7
关于NEON函数的命名规则介绍如下,参考stn_lcd:
数据类型
NEON Intrinsics内置的整数数据类型主要包括以下几种:
(u)int8x8_t;
(u)int8x16_t;
(u)int16x4_t;
(u)int16x8_t;
(u)int32x2_t;
(u)int32x4_t;
(u)int64x1_t;
其中,第一个数字代表的是数据类型宽度为8/16/32/64位,第二个数字代表的是一个寄存器中该类型数据的数量。如int16x8_t代表16位有符号数,寄存器中共有8个数据。
常用指令
NEON Intrinsics支持的所有指令可参看ARM NEON Intrinsics,其包含了常用的arm汇编指令类型,如数学运算,逻辑运算等。另外,其引入了有针对性的加载/存储/转置/交叉存取等指令。部分常见的指令在会下面的示例环节中予以说明。需要注意的是,指令中的助记符与arm汇编是相同的。
示例1:
int16x8_t vqaddq_s16 (int16x8_t, int16x8_t)
int16x4_t vqadd_s16 (int16x4_t, int16x4_t)
第一个字母'v'指明是vector向量指令,也就是NEON指令;
第二个字母'q'指明是饱和指令,即后续的加法结果会自动饱和;
第三个字段'add'指明是加法指令;
第四个字段'q'指明操作寄存器宽度,为'q'时操作QWORD, 为128位;未指明时操作寄存器为DWORD,为64位;
第五个字段's16'指明操作的基本单元为有符号16位整数,其最大表示范围为-32768 ~ 32767;
形参和返回值类型约定与C语言一致。
其它可能用到的助记符包括:
l 长指令,数据扩展
w 宽指令,数据对齐
n 窄指令, 数据压缩
示例2
uint8x8_t vld1_u8 (const uint8_t *)
第二个字段'ld'表示加载指令
第三个字段'1'(注意是1,不是l)表示顺次加载。如果需要处理图像的RGB分量,可能会用到vld3。关于vld/vst指令更详细的说明,请自己参阅arm官方文档。
编译指令介绍如下,此处引用贺二公子的总结:
常用的CPU类型编译器选项
CPU类型 | CPU类型选项 | FP选项 | FP + SIMD选项 | 备注 |
---|---|---|---|---|
Cortex-A5 | -mcpu=cortex-a5 | -mfpu=vfpv3-fp16 -mfpu=vfpv3-d16-fp16 | -mfpu=neon-fp16 | -d16表明只有前16个浮点寄存器可用 |
Cortex-A7 | -mcpu=cortex-a7 | -mfpu=vfpv4 -mfpu=vfpv4-d16 | -mfpu=neon-vfpv4 | -fp16表明支持16bit半精度浮点操作 |
Cortex-A8 | -mcpu=cortex-a8 | -mfpu=vfpv3 | -mfpu=neon | |
Cortex-A9 | -mcpu=cortex-a9 | -mfpu=vfpv3-fp16 -mfpu=vfpv3-d16-fp16 | -mfpu=neon-fp16 | |
Cortex-A15 | -mcpu=cortex-a15 | -mfpu=vfpv4 | -mfpu=neon-vfpv4 |