ARM Neon 编程笔记一(ARM NEON Intrinsics, SIMD运算, 优化心得)
1. ARM Neon Intrinsics 编程
1.入门:基本能上手写Intrinsics
1.1 Neon介绍、简明案例与编程惯例
1.2 如何检索Intrinsics
1.3 优化效果案例
1.4 如何在Android应用Neon
2. 进阶:注意细节处理,学习常用算子的实现
2.1 与Neon相关的ARM体系结构
2.2 对非整数倍元素个数(leftovers)的处理技巧
2.3 算子源码学习(ncnn库,AI方向)
2.4 算子源码学习(Nvidia carotene库,图像处理方向 )
3. 学个通透:了解原理
3.1 SIMD加速原理
3.2 了解硬件决定的速度极限:Software Optimization Guide
3.3 反汇编分析生成代码质量
4. 其他:相关的研讨会视频、库、文档等
ncnn是腾讯开源,nihui维护的AI推理引擎。由于Neon实现往往跟循环展开等技巧一起使用,代码往往比较长。可以先阅读普通实现的代码实现了解顶层逻辑,再阅读Neon实现的代码。例如,我们希望学习全连接层(innerproduct)的Neon实现,其普通实现的位置在ncnn/src/layer/innerproduct.cpp,对应的Neon加速实现的位置在ncnn/src/layer/arm/innerproduct_arm.cpp。
2. ARMv8 中的 SIMD 运算
示例
4x4 矩阵乘法
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/time.h>#if __aarch64__
#include <arm_neon.h>
#endifstatic void dump(uint16_t **x)
{int i, j;uint16_t *xx = (uint16_t *)x;printf("%s:\n", __func__);for(i = 0; i < 4; i++) {for(j = 0; j < 4; j++) {printf("%3d ", *(xx + (i << 2) + j));}printf("\n");}
}static void matrix_mul_c(uint16_t aa[][4], uint16_t bb[][4], uint16_t cc[][4])
{int i = 0, j = 0;printf("===> func: %s, line: %d\n", __func__, __LINE__);for(i = 0; i < 4; i++) {for(j = 0; j < 4; j++) {cc[i][j] = aa[i][j] * bb[i][j];}}}#if __aarch64__
static void matrix_mul_neon(uint16_t **aa, uint16_t **bb, uint16_t **cc)
{printf("===> func: %s, line: %d\n", __func__, __LINE__);
#if 1uint16_t (*a)[4] = (uint16_t (*)[4])aa;uint16_t (*b)[4] = (uint16_t (*)[4])bb;uint16_t (*c)[4] = (uint16_t (*)[4])cc;printf("aaaaaaaa\n");asm("nop");asm("nop");asm("nop");asm("nop");uint16x4_t _cc0;uint16x4_t _cc1;uint16x4_t _cc2;uint16x4_t _cc3;uint16x4_t _aa0 = vld1_u16((uint16_t*)a[0]);uint16x4_t _aa1 = vld1_u16((uint16_t*)a[1]);uint16x4_t _aa2 = vld1_u16((uint16_t*)a[2]);uint16x4_t _aa3 = vld1_u16((uint16_t*)a[3]);uint16x4_t _bb0 = vld1_u16((uint16_t*)b[0]);uint16x4_t _bb1 = vld1_u16((uint16_t*)b[1]);uint16x4_t _bb2 = vld1_u16((uint16_t*)b[2]);uint16x4_t _bb3 = vld1_u16((uint16_t*)b[3]);_cc0 = vmul_u16(_aa0, _bb0);_cc1 = vmul_u16(_aa1, _bb1);_cc2 = vmul_u16(_aa2, _bb2);_cc3 = vmul_u16(_aa3, _bb3);vst1_u16((uint16_t*)c[0], _cc0);vst1_u16((uint16_t*)c[1], _cc1);vst1_u16((uint16_t*)c[2], _cc2);vst1_u16((uint16_t*)c[3], _cc3);asm("nop");asm("nop");asm("nop");asm("nop");
#elseprintf("bbbbbbbb\n");int i = 0;uint16x4_t _aa[4], _bb[4], _cc[4];uint16_t *a = (uint16_t*)aa;uint16_t *b = (uint16_t*)bb;uint16_t *c = (uint16_t*)cc;for(i = 0; i < 4; i++) {_aa[i] = vld1_u16(a + (i << 2));_bb[i] = vld1_u16(b + (i << 2));_cc[i] = vmul_u16(_aa[i], _bb[i]);vst1_u16(c + (i << 2), _cc[i]);}#endif
}static void matrix_mul_asm(uint16_t **aa, uint16_t **bb, uint16_t **cc)
{printf("===> func: %s, line: %d\n", __func__, __LINE__);uint16_t *a = (uint16_t*)aa;uint16_t *b = (uint16_t*)bb;uint16_t *c = (uint16_t*)cc;#if 0asm volatile("ldr d3, [%0, #0] \n\t""ldr d2, [%0, #8] \n\t""ldr d1, [%0, #16] \n\t""ldr d0, [%0, #24] \n\t""ldr d7, [%1, #0] \n\t""ldr d6, [%1, #8] \n\t""ldr d5, [%1, #16] \n\t""ldr d4, [%1, #24] \n\t""mul v3.4h, v3.4h, v7.4h \n\t""mul v2.4h, v2.4h, v6.4h \n\t""mul v1.4h, v1.4h, v5.4h \n\t""mul v0.4h, v0.4h, v4.4h \n\t"//"add v3.4h, v3.4h, v7.4h \n\t"//"add v2.4h, v2.4h, v6.4h \n\t"//"add v1.4h, v1.4h, v5.4h \n\t"//"add v0.4h, v0.4h, v4.4h \n\t""str d3, [%2,#0] \n\t""str d2, [%2,#8] \n\t""str d1, [%2,#16] \n\t""str d0, [%2,#24] \n\t": "+r"(a), //%0"+r"(b), //%1"+r"(c) //%2:: "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7");
#else// test, OKasm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm volatile(//"ld4 {v0.4h, v1.4h, v2.4h, v3.4h}, [%0] \n\t""ld4 {v0.4h-v3.4h}, [%0] \n\t""ld4 {v4.4h, v5.4h, v6.4h, v7.4h}, [%1] \n\t""mul v3.4h, v3.4h, v7.4h \n\t""mul v2.4h, v2.4h, v6.4h \n\t""mul v1.4h, v1.4h, v5.4h \n\t""mul v0.4h, v0.4h, v4.4h \n\t""st4 {v0.4h, v1.4h, v2.4h, v3.4h}, [%2] \n\t": "+r"(a), //%0"+r"(b), //%1"+r"(c) //%2:: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
#endif
}
#endifint main(int argc, const char *argv[])
{uint16_t aa[4][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{3, 6, 8, 1},{2, 6, 7, 1}};uint16_t bb[4][4] = {{1, 3, 5, 7},{2, 4, 6, 8},{2, 5, 7, 9},{5, 2, 7, 1}};uint16_t cc[4][4] = {0};int i, j;struct timeval tv;long long start_us = 0, end_us = 0;dump((uint16_t **)aa);dump((uint16_t **)bb);dump((uint16_t **)cc);/* ******** C **********/gettimeofday(&tv, NULL);start_us = tv.tv_sec + tv.tv_usec;matrix_mul_c(aa, bb, cc);gettimeofday(&tv, NULL);end_us = tv.tv_sec + tv.tv_usec;printf("aa[][]*bb[][] C time %lld us\n", end_us - start_us);dump((uint16_t **)cc);#if __aarch64__/* ******** NEON **********/memset(cc, 0, sizeof(uint16_t) * 4 * 4);gettimeofday(&tv, NULL);start_us = tv.tv_sec + tv.tv_usec;matrix_mul_neon((uint16_t **)aa, (uint16_t **)bb, (uint16_t **)cc);gettimeofday(&tv, NULL);end_us = tv.tv_sec + tv.tv_usec;printf("aa[][]*bb[][] neon time %lld us\n", end_us - start_us);dump((uint16_t **)cc);/* ******** asm **********/memset(cc, 0, sizeof(uint16_t) * 4 * 4);gettimeofday(&tv, NULL);start_us = tv.tv_sec + tv.tv_usec;matrix_mul_asm((uint16_t **)aa, (uint16_t **)bb, (uint16_t **)cc);gettimeofday(&tv, NULL);end_us = tv.tv_sec + tv.tv_usec;printf("aa[][]*bb[][] asm time %lld us\n", end_us - start_us);dump((uint16_t **)cc);
#endifreturn 0;
}
1 |
aarch64-linux-gcc -O3 matrix_4x4_mul.c |
gcc –march=armv8-a [input file] -o [output file]
3. NEON编程, 优化心得及内联汇编使用心得
Very thanks to Orchid (Orchid Blog).
NEON intrinsics
提供了一个连接NEON操作的C函数接口,编译器会自动生成相关的NEON指令,支持ARMv7-A或ARMv8-A平台。
所有的intrinsics函数都在GNU官方说明文档。
一个简单的例子:
//add for int array. assumed that count is multiple of 4
#include<arm_neon.h>
// C version 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];}
}// NEON version void add_float_neon1(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;}
}
代码中的vld1q_s32
会被编译器转换成vld1.32 {d0, d1}, [r0]
指令,同理vaddq_s32
和vst1q_s32
被转换成vadd.i32 q0, q0, q0
,vst1.32 {d0, d1}, [r0]
。若不清楚指令意义,请参见ARM® Compiler armasm User Guide - Chapter 12 NEON and VFP Instructions。
ARM Neon 编程笔记一(ARM NEON Intrinsics, SIMD运算, 优化心得)相关推荐
- 【genius_platform软件平台开发】第八十二讲:ARM Neon指令集一(ARM NEON Intrinsics, SIMD运算, 优化心得)
1. ARM Neon Intrinsics 编程 1.入门:基本能上手写Intrinsics 1.1 Neon介绍.简明案例与编程惯例 1.2 如何检索Intrinsics 1.3 优化效果案例 1 ...
- shell脚本编程笔记(二)—— 执行数学运算
数学运算在编程中也很重要,shell对它的支持并不算好,本文整理一下shell进行数学运算的途径,各种方法的缺点. 一. expr命令 shell最开始处理数学表达式的命令,可以在命令行做算术运算,参 ...
- ARM NEON 编程简单入门1
原文:http://blog.csdn.net/silentob/article/details/72954618 ARM NEON 编程简单入门1 NEON简介 NEON是适用于ARM Corte ...
- ARM NEON编程
下午终于把串口的任务完成的差不多了,同时老板有给安排了一个新的任务:看一下ARM NEON,一脸懵逼,这是个什么玩意!!! 我原本想做CUDA下的GPU加速的,结果这给我弄了个ARM的,这可咋整,不管 ...
- ARM SIMD NEON 简介 (翻译自 Introducing NEON Development Article)
目录 NEON简介 SIMD是什么? ARM SIMD 指令集 NEON是什么? NEON架构概览 支持的数据类型 NEON寄存器 NEON指令 NEON开发 汇编器 Intrinsics 自动向量化 ...
- ARM Cortex系列(A8/A9/A15/A7) NEON多媒体处理SIMD引擎优化
出处: http://houh-1984.blog.163.com/blog/static/31127834201211275111378/ Cortex-A9的NEON多媒体处理器是基于ARMv7的 ...
- arm 饱和指令_ARM平台下NEON使用方法详解
NEON介绍 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速.NEON 技术是 ARM Cortex™-A 系列处理器的 128 位 SIMD(单指令,多数据)架构扩展,专门针对大规 ...
- 【学习笔记】ARM Cortex-A(armv7)编程手册
第一章介绍了 ARM Cortex-A 系列处理器的基本功能,介绍 ARM 体系结构的基础知识,涵盖各种寄存器: 第二章和第三章提供了有关各个处理器的模式和一些背景知识: 第四章和第五章简要介绍了 A ...
- 大前端CPU优化技术--NEON编程优化技巧
前言 在前面的文章中我们介绍了NEON的基础,NEON技术的全景,指令及NEON intrinsic指令,相信大家能通过前面的学习写一些简单的NEON程序.但要想写好一个性能高的NEON程序,远不止你 ...
最新文章
- R语言使用fs包的dir_delete函数删除指定的文件目录(remove the directory)、举一反三、file_delete函数、link_delete函数可以用来删除文件和文件夹
- 图像混合模式:Android Paint Xfermode 使用和demo
- 正则表达式 2. 单字符或
- ubuntu中mysql操作_uBuntu下安装MySql,及mySql操作!
- 【深度学习】编写同时在PyTorch和Tensorflow上工作的代码
- 用Nginx+Lua实现高性能、高可靠、安全的登陆验证
- Spring Boot (一)Spring Boot 概述
- 2019牛客多校第七场E Find the median 权值线段树+离散化
- Win10下python3和python2多版本同时安装并解决pip共存问题
- python测试系列教程 —— YAML配置文件语法教程
- 首款国产开源数据库TBase核心架构演进
- 启发式搜索A * 算法
- 小米真蓝牙耳机说明书_【小米真无线蓝牙耳机Air 2评测使用说明书介绍】快充|降噪|蓝牙5.0|开盖即连|快捷操作_摘要频道_什么值得买...
- html 设置打印纸张大小怎么设置,打印机纸张大小怎么设置 打印机纸张大小设置方法【详细介绍】...
- 超级节点的困境,社区治理的尴尬 |链捕手
- Intel处理器家族及命名规则
- C++20 span
- c语言flappy bird编程,C语言实现Flappy Bird小游戏
- Openwrt:LuCI入门(一)
- java 节气_java计算某个日期是什么节气(24节气)