转载请标明出处:https://blog.csdn.net/u013752202/article/details/92008192
文章目的:
让小白也能迅速开始Neon编程。
一、什么是Neon
Neon是ARM ARMv7-A架构以上的处理器(从Cortex-A5开始)中集成的一套SIMD(Single Instruction, Multiple Data)单指令多数据指令集,相当于X86上的SSE,MIPS上的MXU,PowerPC上的AltiVec。
二、Neon有什么作用
Neon作为单指令多数据指令集使CPU可以在一个指令周期内并行处理多个数据,节省算法处理时间,常常作为图像算法处理的一种加速手段。
三、怎么使用Neon
使用Neon进行算法加速可以采用三种方式
(1)直接使用Neon指令集进行内嵌汇编编程
(2)使用instruction C进行编程
(3)调用Ne10库函数进行编程
四、Neon的"Hello world"
简介:用普通方式和Neon方式分别计算两个矩阵之和,分别测量出耗时
main.cpp
#include <stdio.h>
#include <string.h>
#include <arm_neon.h>
#include<sys/time.h>
#define TIME_START(num) struct timeval tv_start##num,tv_end##num;gettimeofday(&tv_start##num,NULL);
#define TIME_END(num,tag) gettimeofday(&tv_end##num,NULL);\
printf("[%s-%d]:%s Cost=%fms\n",__func__,__LINE__,#tag,((tv_end##num.tv_sec-tv_start##num.tv_sec)*1e6+(tv_end##num.tv_usec-tv_start##num.tv_usec))/1000.0f);\
gettimeofday(&tv_start##num,NULL);
class Mat{
public:
Mat(int wh,int hh){
w=wh;
h=hh;
size=w*h;
data=new unsigned char [w*h];
}
~Mat(){
delete [] data;
}
void clear(unsigned char val){
memset(data,val,size);
}
int w;
int h;
int size;
unsigned char *data;
};
int matCmp(Mat &mat0,Mat &mat1)
{
if(mat0.w!=mat1.w||mat0.h!=mat1.h){
return 0;
}
int w=mat0.w;
int h=mat0.h;
for(int j=0;j<h;j++){
for(int i=0;i<w;i++){
int idx=j*w+i;
if(mat0.data[idx]!=mat1.data[idx]){
return 0;
}
}
}
return 1;
}
void matAdd(Mat &mat0,Mat &mat1,Mat &dst)
{
if(mat0.w!=mat1.w||mat0.h!=mat1.h){
return;
}
int w=mat0.w;
int h=mat0.h;
for(int j=0;j<h;j++){
for(int i=0;i<w;i++){
int idx=j*w+i;
dst.data[idx]=mat0.data[idx]+mat1.data[idx];
}
}
}
//uint8x8_t vld1_u8 (const uint8_t * __a);
//uint8x8_t vadd_u8 (uint8x8_t __a, uint8x8_t __b);
//void vst1_u8 (uint8_t * __a, uint8x8_t __b);
void vmatAdd(Mat &mat0,Mat &mat1,Mat &dst)
{
if(mat0.w!=mat1.w||mat0.h!=mat1.h){
return;
}
int w=mat0.w;
int h=mat0.h;
uint8x8_t vmat0,vmat1,vdst;
for(int j=0;j<h;j++){
int jdx=j*w;
for(int i=0;i<w;i+=8){
int idx=jdx+i;
vmat0=vld1_u8(mat0.data+idx);
vmat1=vld1_u8(mat1.data+idx);
vdst=vadd_u8(vmat0,vmat1);
vst1_u8(dst.data+idx,vdst);
}
}
}
//uint8x16_t vld1q_u8 (const uint8_t * __a);
//uint8x16_t vaddq_u8 (uint8x16_t __a, uint8x16_t __b);
//void vst1q_u8 (uint8_t * __a, uint8x16_t __b);
void vmatAddq(Mat &mat0,Mat &mat1,Mat &dst)
{
if(mat0.w!=mat1.w||mat0.h!=mat1.h){
return;
}
int w=mat0.w;
int h=mat0.h;
uint8x16_t vmat0,vmat1,vdst;
for(int j=0;j<h;j++){
int jdx=j*w;
for(int i=0;i<w;i+=16){
int idx=jdx+i;
vmat0=vld1q_u8(mat0.data+idx);
vmat1=vld1q_u8(mat1.data+idx);
vdst=vaddq_u8(vmat0,vmat1);
vst1q_u8(dst.data+idx,vdst);
}
}
}
int main(int argc,char **argv)
{
Mat mat0(3840,2160);
Mat mat1(3840,2160);
mat0.clear(100);
mat1.clear(50);
Mat dst(3840,2160);
Mat dst1(3840,2160);
Mat dst2(3840,2160);
TIME_START(0);
matAdd(mat0,mat1,dst);
TIME_END(0,matAdd);
vmatAdd(mat0,mat1,dst1);
TIME_END(0,vmatAdd);
vmatAddq(mat0,mat1,dst2);
TIME_END(0,vmatAddq);
if(matCmp(dst,dst1)){
printf("dst=dst1\n");
}
else{
printf("dst!=dst1\n");
}
if(matCmp(dst,dst2)){
printf("dst=dst2\n");
}
else{
printf("dst!=dst2\n");
}
return 0;
}
Makefile
CC = ../../prebuilts/toolschain/usr/bin/arm-linux-gcc
CPP = ../../prebuilts/toolschain/usr/bin/arm-linux-g++
STRIP =../../prebuilts/toolschain/usr/bin/arm-linux-strip
INC=-I./
SRC=$(wildcard ./*.cpp)
OBJ=$(SRC:%.cpp=%.o)
CPP_FLAG=-Wall -O2 -mfloat-abi=hard -mfpu=neon
TARGET=matcal
all:$(TARGET)
$(TARGET):$(OBJ)
$(CPP) $(OBJ) -o $@
$(OBJ):%.o:%.cpp
$(CPP) $(INC) $(CPP_FLAG) -c $< -o $@
.PHONY:clean
clean:
rm -f $(TARGE) $(OBJ)
运行结果:
结果显示,计算两个4K(3840x2160)矩阵之和:
普通方式耗时:270ms
Neon方式一次并行8个耗时:95ms
Neon方式一次并行16个耗时:95ms
转载请标明出处:https://blog.csdn.net/u013752202/article/details/92008192