在学习了基本的NEON指令集用法之后,就可以使用NEON指令集来实现一些简单的函数了,在这里我们实现一下简单的加减乘除运算函数。需要注意的是,NEON指令对不同类型的数据使用了不同的intrinsic函数,所以编写的函数对于不同类型的数据具体有所区别,但是在逻辑上是相同的。对于不同的数据所用到的intrinsic函数可参见官网:
Intrinsics – Arm Developerhttps://developer.arm.com/architectures/instruction-sets/intrinsics/ 接下来的以float64(double)类型的数据为例
ADD函数
void Add_64f(const double* pSrc1, const double* pSrc2, double* pDst, int len){
float64x2_t va, vb, vc;
int l = len - len % 2;
for(int i=0;i<l;i+=2){
va = vld1q_f64(&pSrc1[i]);
vb = vld1q_f64(&pSrc2[i]);
vc = vAddq_f64(va, vb);
vst1q_f64(&pDst[i], vc);
}
for(int i=l;i<len;i++){
pDst[i] = pSrc1[i] + pSrc2[i];
}
}
最简单的加法函数,读取两个double数组并加载到向量里进行加法运算,最后将结果存储到结果数组中。需要注意的是循环递增的数量要根据向量寄存器里的数据个数来定,如float32位的数据在128位的寄存器中可以存4个,那么这时候循环就应该每次按4递增。除此以外要注意在主循环结束后处理余项
Sub函数
void Sub_64f(const double* pSrc1, const double* pSrc2, double* pDst, int len){
float64x2_t va, vb, vc;
int l = len - len % 2;
for(int i=0;i<l;i+=2){
va = vld1q_f64(&pSrc1[i]);
vb = vld1q_f64(&pSrc2[i]);
vc = vSubq_f64(va, vb);
vst1q_f64(&pDst[i], vc);
}
for(int i=l;i<len;i++){
pDst[i] = pSrc1[i] - pSrc2[i];
}
}
基本一致
Mul函数
void Mul_64f(const double* pSrc1, const double* pSrc2, double* pDst, int len){
float64x2_t va, vb, vc;
int l = len - len % 2;
for(int i=0;i<l;i+=2){
va = vld1q_f64(&pSrc1[i]);
vb = vld1q_f64(&pSrc2[i]);
vc = vmulq_f64(va, vb);
vst1q_f64(&pDst[i], vc);
}
for(int i=l;i<len;i++){
pDst[i] = pSrc1[i] * pSrc2[i];
}
}
Div函数
void Div_64f(const double* pSrc1, const double* pSrc2, double* pDst, int len){
float64x2_t va, vb, vc;
int l = len - len % 2;
for(int i=0;i<l;i+=2){
va = vld1q_f64(&pSrc1[i]);
vb = vld1q_f64(&pSrc2[i]);
vb = vrecpeq_f64(vb);
vc = vmulq_f64(va, vb);
vst1q_f64(&pDst[i], vc);
}
for(int i=l;i<len;i++){
pDst[i] = pSrc1[i] / pSrc2[i];
}
}
除法需要注意的是,为了精度考虑,要将除数通过recpe取倒数转化为乘法