ARM Neon并行加速第一课

转载请标明出处: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

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶落西湘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值