NEON快速入门
NEON
- 1.簡(jiǎn)介
- 2.簡(jiǎn)單實(shí)例
- 3.總結(jié)
1.簡(jiǎn)介
沒(méi)有長(zhǎng)篇大論,只用于NEON快速入門!
SIMD:
- 單指令處理多個(gè)數(shù)據(jù)的并行技術(shù)
- 例如在C語(yǔ)言中對(duì)一個(gè)int[8]的數(shù)組里每一個(gè)數(shù)都執(zhí)行加1操作,SIMD技術(shù)可以通過(guò)一條add指令并行處理;而通常我們自己寫for循環(huán)需要執(zhí)行8次add才能完成,耗時(shí)更多
NEON:
- 一種基于SIMD并適用于ARM的技術(shù),從ARM-V7開(kāi)始被采用,目前可以在ARM Cortex-A和Cortex-R系列處理器中使用
- 寄存器,用于存放需要操作的數(shù)據(jù);16個(gè)128bit的寄存器(128bit代表最大長(zhǎng)度,對(duì)于int類型數(shù)據(jù),能存放4個(gè),對(duì)于char類型數(shù)據(jù)能存放16個(gè),也就是說(shuō)我能直接把int[4]或者char[16]直接放到128bit的寄存器中)、32個(gè)64bit寄存器
- 支持的數(shù)據(jù)類型,上官網(wǎng)查詢
- 常用操作:加減乘除,數(shù)據(jù)之間的讀寫等
2.簡(jiǎn)單實(shí)例
實(shí)例內(nèi)容:兩個(gè)長(zhǎng)度為5000的int型數(shù)組相加,把每個(gè)結(jié)果存入另一個(gè)長(zhǎng)度為5000的數(shù)組當(dāng)中,對(duì)比純C語(yǔ)言實(shí)現(xiàn)和NEON實(shí)現(xiàn)的性能。
純C實(shí)現(xiàn)函數(shù):平平無(wú)奇,誰(shuí)都能寫
NEON實(shí)現(xiàn)函數(shù):
int32x4_t in1, in2, out:用于存放4個(gè)int型數(shù)據(jù)的變量,int32x4_t是一個(gè)數(shù)據(jù)類型聲明(和int效果一致),類似的還有int16x8_t(用于存放8個(gè)short類型數(shù)據(jù))、int8x16_t(用于存放16個(gè)char類型數(shù)據(jù)),更詳細(xì)的去官網(wǎng)查詢
in1 = vld1q_s32(src1):將指針src1的4個(gè)數(shù)據(jù)加載到in1當(dāng)中,vld1q_s32用于加載int型的數(shù)據(jù),并且只會(huì)是4個(gè)數(shù)據(jù),其余類型的加載函數(shù)自行查詢,但都是相對(duì)應(yīng)的
src1 += 4:數(shù)據(jù)加載完成后,指針向后移動(dòng)4位,用于后續(xù)數(shù)據(jù)加載
out = vaddq_s32(in1, in2):執(zhí)行in1和in2兩個(gè)向量相加操作,并存入out中
vst1q_s32(dst, out):將out里的數(shù)據(jù)存入dst指針指向的內(nèi)存中
完畢!
整體代碼
#include<time.h> #include<stdio.h> #include<stdlib.h> #include<arm_neon.h>void add_int_c(int* dst, int* src1, int* src2, int count) {int i;for (i = 0; i < count; i++)dst[i] = src1[i] + src2[i]; }void add_int_neon(int* dst, int* src1, int* src2, int count) {int i;for (i = 0; i < count; i += 4){int32x4_t in1, in2, out;in1 = vld1q_s32(src1);src1 += 4;in2 = vld1q_s32(src2);src2 += 4;out = vaddq_s32(in1, in2);vst1q_s32(dst, out);dst += 4;} }int main() {/* 數(shù)據(jù)內(nèi)存分配,初始化 */int size = 5000;int *dst = (int*)malloc(size * sizeof(int));int *src1 = (int*)malloc(size * sizeof(int));int *src2 = (int*)malloc(size * sizeof(int));for(int i = 0; i < size; i++){src1[i] = i;src2[i] = i;}/* 時(shí)間初始化 */struct timespec time1_img = {0, 0};struct timespec time2_img = {0, 0};clock_gettime(CLOCK_REALTIME, &time1_img);for(int i = 0; i < size; i++){add_int_c(dst, src1, src2, size);}clock_gettime(CLOCK_REALTIME, &time2_img);printf("C time:%d ms\n", (time2_img.tv_sec - time1_img.tv_sec)*1000 + (time2_img.tv_nsec - time1_img.tv_nsec)/1000000);printf("dst[0]:%d\n", dst[0]);memset(dst, 0, size);clock_gettime(CLOCK_REALTIME, &time1_img);for(int i = 0; i < size; i++){add_int_neon(dst, src1, src2, size);}clock_gettime(CLOCK_REALTIME, &time2_img);printf("Neon time:%d ms\n", (time2_img.tv_sec - time1_img.tv_sec)*1000 + (time2_img.tv_nsec - time1_img.tv_nsec)/1000000);printf("dst[0]:%d\n", dst[0]);free(dst);free(src1);free(src2);return 0; }Makefile
CC = @echo " GCC $@"; $(CROSS)gccCROSS = arm-xmv2-linux- CFLAGS += -mcpu=cortex-a9 -mfloat-abi=softfp -mfpu=neon -mno-unaligned-access -fno-aggressive-loop-optimizations -flax-vector-conversions -fsigned-char -fopenmp CFLAGS += -std=gnu99 -Wall -O2TESTSOURCE = $(wildcard ./main.c) TESTTARGET = main.outTARGET = $(TESTTARGET) all:$(TARGET)$(TESTTARGET):$(CC) $(CFLAGS) -save-temps -o $(TESTTARGET) $(TESTSOURCE) -lstdc++ -lmclean:rm -f $(TESTTARGET)效果對(duì)比
3.總結(jié)
- demo中可以明顯看到程序執(zhí)行耗時(shí)減少一半,效果非常明顯
- neon只適用于重復(fù)運(yùn)算,我們只需要找到那些具有重復(fù)運(yùn)算的地方,然后使用neon進(jìn)行加速
- 第一次看到這些函數(shù)非常陌生,多去官網(wǎng)查幾次就熟悉了
總結(jié)
- 上一篇: Neon 指令
- 下一篇: rtx3060ti参数配置