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_s32vst1q_s32被转换成vadd.i32 q0, q0, q0vst1.32 {d0, d1}, [r0]。若不清楚指令意义,请参见ARM® Compiler armasm User Guide - Chapter 12 NEON and VFP Instructions。

ARM Neon 编程笔记一(ARM NEON Intrinsics, SIMD运算, 优化心得)相关推荐

  1. 【genius_platform软件平台开发】第八十二讲:ARM Neon指令集一(ARM NEON Intrinsics, SIMD运算, 优化心得)

    1. ARM Neon Intrinsics 编程 1.入门:基本能上手写Intrinsics 1.1 Neon介绍.简明案例与编程惯例 1.2 如何检索Intrinsics 1.3 优化效果案例 1 ...

  2. shell脚本编程笔记(二)—— 执行数学运算

    数学运算在编程中也很重要,shell对它的支持并不算好,本文整理一下shell进行数学运算的途径,各种方法的缺点. 一. expr命令 shell最开始处理数学表达式的命令,可以在命令行做算术运算,参 ...

  3. ARM NEON 编程简单入门1

    原文:http://blog.csdn.net/silentob/article/details/72954618  ARM NEON 编程简单入门1 NEON简介 NEON是适用于ARM Corte ...

  4. ARM NEON编程

    下午终于把串口的任务完成的差不多了,同时老板有给安排了一个新的任务:看一下ARM NEON,一脸懵逼,这是个什么玩意!!! 我原本想做CUDA下的GPU加速的,结果这给我弄了个ARM的,这可咋整,不管 ...

  5. ARM SIMD NEON 简介 (翻译自 Introducing NEON Development Article)

    目录 NEON简介 SIMD是什么? ARM SIMD 指令集 NEON是什么? NEON架构概览 支持的数据类型 NEON寄存器 NEON指令 NEON开发 汇编器 Intrinsics 自动向量化 ...

  6. ARM Cortex系列(A8/A9/A15/A7) NEON多媒体处理SIMD引擎优化

    出处: http://houh-1984.blog.163.com/blog/static/31127834201211275111378/ Cortex-A9的NEON多媒体处理器是基于ARMv7的 ...

  7. arm 饱和指令_ARM平台下NEON使用方法详解

    NEON介绍 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速.NEON 技术是 ARM Cortex™-A 系列处理器的 128 位 SIMD(单指令,多数据)架构扩展,专门针对大规 ...

  8. 【学习笔记】ARM Cortex-A(armv7)编程手册

    第一章介绍了 ARM Cortex-A 系列处理器的基本功能,介绍 ARM 体系结构的基础知识,涵盖各种寄存器: 第二章和第三章提供了有关各个处理器的模式和一些背景知识: 第四章和第五章简要介绍了 A ...

  9. 大前端CPU优化技术--NEON编程优化技巧

    前言 在前面的文章中我们介绍了NEON的基础,NEON技术的全景,指令及NEON intrinsic指令,相信大家能通过前面的学习写一些简单的NEON程序.但要想写好一个性能高的NEON程序,远不止你 ...

最新文章

  1. R语言使用fs包的dir_delete函数删除指定的文件目录(remove the directory)、举一反三、file_delete函数、link_delete函数可以用来删除文件和文件夹
  2. 图像混合模式:Android Paint Xfermode 使用和demo
  3. 正则表达式 2. 单字符或
  4. ubuntu中mysql操作_uBuntu下安装MySql,及mySql操作!
  5. 【深度学习】编写同时在PyTorch和Tensorflow上工作的代码
  6. 用Nginx+Lua实现高性能、高可靠、安全的登陆验证
  7. Spring Boot (一)Spring Boot 概述
  8. 2019牛客多校第七场E Find the median 权值线段树+离散化
  9. Win10下python3和python2多版本同时安装并解决pip共存问题
  10. python测试系列教程 —— YAML配置文件语法教程
  11. 首款国产开源数据库TBase核心架构演进
  12. 启发式搜索A * 算法
  13. 小米真蓝牙耳机说明书_【小米真无线蓝牙耳机Air 2评测使用说明书介绍】快充|降噪|蓝牙5.0|开盖即连|快捷操作_摘要频道_什么值得买...
  14. html 设置打印纸张大小怎么设置,打印机纸张大小怎么设置 打印机纸张大小设置方法【详细介绍】...
  15. 超级节点的困境,社区治理的尴尬 |链捕手
  16. Intel处理器家族及命名规则
  17. C++20 span
  18. c语言flappy bird编程,C语言实现Flappy Bird小游戏
  19. Openwrt:LuCI入门(一)
  20. java 节气_java计算某个日期是什么节气(24节气)

热门文章

  1. kappa系数在评测中的应用
  2. 批处理 检测U盘插入并自动备份文件
  3. 网站如何判断客户端是在国内还是国外
  4. 路由器温度测试软件,【美国网件 R8500 无线路由器产品测试】信号|速度|温度_摘要频道_什么值得买...
  5. 关于线程同步的几种方法
  6. WPF和Halcon编程第一步,导入图像到Halcon的WPF窗口控件出错?
  7. 插件分享 | 简单免杀绕过和利用上线的 GoCS
  8. JavaIO系统解析
  9. IDEA使用手记——IDEA主菜单恢复
  10. 导入导出数据库或数据库表