neon 指令 c语言,NEON初步使用
前言
指令集并行是CPU的优化加速的一个方向,在ARM芯片主要是利用NEON指令集实现指令集并行
NEON简介
NEON就是高级SIMD,单指令多数据,适用于图像、音频等数据处理。ARMv6就叫SIMD,ARMv7开始叫NEON,aarch64又有点不一样,下文只针对ARMv7或者ARMv8 aarch32的NEON进行
NEON有32个64位长的寄存器(D0-D31,每个D可以装2个浮点数据),也可以看做为16个128位长的寄存器(Q0-Q15,每个Q可以装4个浮点数据),所以一句指令最多可以同时实现4个乘法操作,理论速度可以提升4倍
如何使用NEON
三种使用NEON的方法
库函数
官方给了2个库可以使用,OpenMax DL和Ne10,后者在github上有,也是我用来参考学习的主要对象
汇编函数
用汇编语句编写.s文件
在C/C++代码中嵌入汇编语句(inline assembly)
内联函数(intrinsics function)
在C代码中直接嵌入内联函数用以实现功能,但是性能会取决于编译器和具体设备
汇编函数基础
想要高效利用NEON的话,汇编是避不开的。下面是ARM汇编相关的准备知识
参考资料
汇编基础原理
b、bx、bl指令
arm汇编指令
GNU ARM Assembler Quick Reference
特殊寄存器
TODO: sb、ip是干嘛的
汇编函数文件directive(指令、伪操作)
常见directive(指令、伪操作)
参数
说明
.text
后面那些指令都属于.text段
.syntax
unified
说明下面的指令是ARM和THUMB通用格式
TODO:不太懂
.align
4
4字节对齐
.balign
4
TODO:不太懂
.global
xx_func
函数xx_func可以被外部文件调用访问
.thumb_func
指明一个函数是thumb指令集的函数
TODO:不太懂
编译调用汇编函数
编译汇编文件neon.s的命令需要加选项-mfpu=neon: arm-linux-gnueabihf-gcc -mfpu=neon -c neon.s -o neon.o
主文件main.o链接neon.o的命令: arm-linux-gnueabihf-gcc neon.o main.o -o test
相关编译参数
堆栈读取参数
TODO:汇编读取多参数
参考链接
ARM汇编基础指令
ldr
ldr R0, [R1]! @将内存地址为R1的数据加载到R0,并将R1指向下一个位置
ldr R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
str
str R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
b 无条件跳转
b label_fun
bl 带返回的跳转,保存当前位置到lr,用于子函数调用
bl label_fun @bx lr或者mov pc, lr实现返回
bx 跳转并切换状态,一般用于子函数返回
bx lr
bgt 比较跳转,如果经过之前某句操作后状态寄存器是大于(great than)就跳转
cmp r0, #5
bgt label_foo
and 按位与
and r3, r2, #3 @ r3 = r2 % 4
asr 右移
asr r2, r2, #2 @ r2 = r2 >> 2
cbz 比较跳转,如果为零就跳到后面的指令
cbz r3, label_foo
sub 减命令
sub r0, r1, r2 @ r0 = r1 - r2
subs 减命令,并更新状态寄存器
同sub一样,多的更新状态寄存器功能可以配合bgt
NEON矢量读取命令vld1
vld1.32 {d0}, [r1]@从内存地址r1开始读取2个32位数据到d0里,因为d能存2个32位浮点数
vld1.32 {q0}, [r1]@从内存地址r1开始读取4个32位数据到q0里,因为q能存2个32位浮点数
NEON矢量存储命令vst1
vst1.32 {d0}, [r1]@将d0里的2个32位浮点数写到内存地址r1里
vst1.32 {q0}, [r1]@将q0里的4个32位浮点数写到内存地址r1里
NEON矢量加命令vadd
图文并茂,不用多说 vadd.i16 d3, d0, d1 @ d3 = d0 + d1
NEON简单对比实验
github链接记得点赞
C语言实现 void mul_float_c(float *dst, float *src1, float *src2, int count)
{
int i = 0, j = 0;
for (j = 0; j < count; j++)
for (i = 0; i < 4; i++)
*(dst++) = *(src1++) * *(src2++);
}
汇编实现assembly .text
.syntax unified
.align 4
.global mul_float_neon
.thumb
.thumb_func
mul_float_neon:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ r0: *dst & current dst entry's address浮点型指针,存储结果
@ r1: *src1 & current src1 entry's address浮点型指针,操作对象1
@ r2: *src2 & current src2 entry's address浮点型指针,操作对象2
@ r3: count,循环次数
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.loop:
cbz r3, .return
subs r3, r3, #1
vld1.32 {q0}, [r1]!
vld1.32 {q1}, [r2]! @ for current set
@ calculate values for current set
vmul.f32 q3, q0, q1 @ q3 = q0 + q1
@ store the result for current set
vst1.32 {q3}, [r0]!
b .loop
.return:
mov r0, #0
bx lr
C语言内嵌汇编实现inline assembly void mul_float_neon_inline(float *dst, float *src1, float *src2, int count)
{
asm volatile(
".loop:\n"
"cbz %[count], .return\n"
"subs %[count], %[count], #1\n"
"vld1.32 {q0}, [%[src1]]!\n"
"vld1.32 {q1}, [%[src2]]! @ for current set\n"
"vmul.f32 q3, q0, q1 @ q3 = q0 + q1\n"
"vst1.32 {q3}, [%[dst]]!\n"
"b .loop\n"
".return:\n"
// "mov %[dst], #0\n"//不需要函数的返回跳转
// "bx lr\n"
: // 解释返回参数,如[ dst ] "+r"(dst),有个加号
: [ dst ] "r"(dst), [ src1 ] "r"(src1), [ src2 ] "r"(src2), [ count ] "r"(count)// 解释输入参数
: "memory", "q0", "q1", "q3");// 不太懂,但是要加
}
内联函数实现
内联函数官方在线文档 #include//要用neon内联函数必须要该头文件
void add_float_neon(float* dst, float* src1, float* src2, int count)
{
int i;
for (i = 0; i < count; i += 4)
{
float32x4_t in1, in2, out;
in1 = vld1q_f32(src1);
src1 += 4;
in2 = vld1q_f32(src2);
src2 += 4;
out = vaddq_f32(in1, in2);
vst1q_f32(dst, out);
dst +=4;
}
}
结果对比和分析 pi@raspberrypi:~/mnt/neon_test $ ./neon_test
mul_float_c used: 0.000095 s
mul_float_neon used: 0.000012 s
mul_float_neon_inline used: 0.000011 s
mul_float_neon_intrinsics used: 0.000059 s
mul_float_c and mul_float_neon result same!!!
mul_float_c and mul_float_inline result same!!!
mul_float_c and mul_float_intrinsics result same!!!
分别用C语言的for循环、neon的汇编实现、neon的内联汇编(inline assembly)、neon的内联函数(intrinsics function)这4种方式在A53上实现4*100次浮点运算,可以看出,最快的还是neon汇编实现,约10倍的速度提升,同时两种neon汇编的速度一样没啥区别,但是还是建议不用内联汇编,因为gdb没法debug,至于为什么会达到10倍的提升,一方面是neon的矢量乘法有4倍理论提升,还有就是读数据和存数据都是4倍提速。而neon的内联函数却只有不到2倍的速度提升,真辣鸡
TODO:上述实验数据是在编译优化参数为debug模式"-O0 -g"的情况下测出来的,但是release模式"-Ofast"会报错
总结
来,弄优化,学汇编,用NEON!手动狗头
neon 指令 c语言,NEON初步使用相关推荐
- neon 指令 c语言,Neon指令集优化快速入门教程
Neon指令集优化快速入门教程 Neon指令集优化快速入门教程 文章目录 1. Neon是什么? 2.Neon为什么速度快 3. Neon基础概念 4. Neon的C语言接口 C语言程序中集成Neon ...
- NEON指令优化指南学习之一
RM平台NEON指令的编译和优化 本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex-A8, Cortex-A9, ...
- math-neon基于NEON指令的数学库
这是一个开源的库,地址为https://code.google.com/p/math-neon/,根据项目介绍应该是利用neon指令实现的数学库:包括三角.对数.指数等基于浮点的运算实现,以及矩阵运算 ...
- 基于NEON指令的图像旋转加速【armv7】
目录 前言 知识直通车 NEON转置指令 右旋90 4x4矩阵右旋实例 灰度图(单通道)右旋90 彩图(RGB三通道)右旋90 左旋90 4x4矩阵左旋实例 灰度图(单通道)左旋90 彩图(RGB三通 ...
- ARM和NEON指令 very gooooooood.............
http://blog.csdn.net/chshplp_liaoping/article/details/12752749 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速.目前在移 ...
- ARM和NEON指令 very nice
在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速.目前在移动上使用最多的是ARM芯片. ARM是微处理器行业的一家知名企业,其芯片结构有:armv5.armv6.armv7和armv8 ...
- Android NDK开发之 ARM与NEON指令说明
原文引自: http://blog.csdn.net/chshplp_liaoping/article/details/12752749 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加 ...
- 大前端CPU优化技术--NEON指令介绍
前言 ARM NEON 可以提升音视频,图像,计算机视觉等计算密集型程序的性能,在上一篇大前端CPU优化技术--NEON技术的介绍中,我们知道一些编译器可以将 C/C++ 代码自动转换为 NEON 指 ...
- ARM和NEON指令
转载自:http://blog.csdn.net/chshplp_liaoping/article/details/12752749 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速. ...
最新文章
- Java Web项目结构
- 谁登录了你的linux
- UA MATH563 概率论的数学基础 中心极限定理24 随机变量的特征函数
- ES6新特性之Promise
- 如何自行查询SAP Spartacus Organization Unit List的取数逻辑
- 2021年度训练联盟热身训练赛第一场 E Early Orders 思维 + 栈
- python 行为驱动_什么是行为驱动的Python?
- 应用安全测试的发展趋势
- G2 可视化引擎-统计图表
- caffe数据集——LMDB
- mysql必知必会的数据_MySQL必知必会---数据过滤
- ApiException
- Android 文字转语音之TextToSpeech
- starops 云效运维 文档_云效手册专有云版.pdf
- Kuangbin专题八生成树
- java 6面骰子_java 垒骰子
- 智能路由器OpenWrt 开发环境 及 编译分析(一)
- NVIDIA面经整理
- Android 9.0 设置讯飞语音引擎为默认tts语音播报引擎
- 处理器cpu测试工具