arm 优化 图像处理

2013-06-28 21:35:58 sonikk 阅读数 8447
  • 深度学习部署系统构建

    作为SDCC系列技术峰会的一部分,来自阿里巴巴、微软、商汤科技、第四范式、微博、出门问问、菱歌科技的AI专家,将针对机器学习平台、系统架构、对话机器人、芯片、推荐系统、Keras、分布式系统、NLP等热点话题进行...

    44人学习 CSDN讲师
    免费试看

1. 使用NDK_DEBUG=1后,编译出来的程序比不使用慢很多,大概效率会降低2.5倍,这个数字真不小了!

2. 在NDK_DEBUG=0的情况下,Java调用一次Native函数,耗费时间在0.147~0.233ms左右

3. 使用系统提供的函数,比自己手动copy内存效率快得多,如使用memcpy或者memset可以提高很多速度

4. 尽量避免除法,比较好的做法是使用倒数进行乘法计算,而倒数可以实现用计算器进行求值,直接写入代码

5. Android的一些cpu在处理float类型的时候非常缓慢,下面是我的一个测试程序,程序跑的是求灰度值,可以明显的对比出来优化前后的效果,简直是立竿见影!

	
测试条件:图片分辨率500x750
[pad] 设备:gt_p6800 双核1.4GHz
[phone] 设备:nexus s 单核1.0GHz
[优化前]-------------------------------------------
        // [pad][debug=0] 
	// [time] 14.132000 (ms)
	// [time] 15.066000 (ms)
	// [time] 25.767000 (ms)


	// [phone][debug=0]
	// [time] 175.110992 (ms)
	// [time] 196.481003 (ms)
	// [time] 113.485001 (ms)
	// [time] 162.052994 (ms)
	// [time] 178.488998 (ms) (平均=164.8)
[优化后]-------------------------------------------
 	// [pad][debug=0] 
 	// [time] 12.625000 (ms)
 
 	// [phone][debug=0] 
 	// [time] 48.099998 (ms)
 	// [time] 52.244999 (ms)
 	// [time] 27.365000 (ms)
 	// [time] 34.356998 (ms)
 	// [time] 23.222000 (ms)
 	// [time] 31.214001 (ms) (平均=35.8)

结论:优化后提升的效率:164.8/35.8 = 4.6   提升360%

6. 在必要的时候可以使用多线程进行优化,这样也可以提升cpu的利用率,从而达到优化的目的:

	
测试条件:图片分辨率500x750
[pad] 设备:gt_p6800 双核1.4GHz
[phone] 设备:nexus s 单核1.0GHz
[优化前]-------------------------------------------
        // [pad][debug=0] 
	// [time] 14.132000 (ms)
	// [time] 15.066000 (ms)
	// [time] 25.767000 (ms)


	// [phone][debug=0]
	// [time] 175.110992 (ms)
	// [time] 196.481003 (ms)
	// [time] 113.485001 (ms)
	// [time] 162.052994 (ms)
	// [time] 178.488998 (ms) (平均=164.8)
[优化后]-------------------------------------------
	// [pad][debug=0]
	// [time] 19.000000 (ms)
	// [time] 10.667000 (ms)
	// [time] 13.659000 (ms)
	// [time] 15.953000 (ms)
	// [time] 28.219999 (ms)
	// [time] 17.202999 (ms)


	// [phone][debug=0]
	// [time] 192.744003 (ms)
	// [time] 124.049004 (ms)
	// [time] 91.658997 (ms)
	// [time] 88.318001 (ms)
	// [time] 92.111000 (ms)
	// [time] 91.551003 (ms)
 	// [time] 31.214001 (ms) (平均=101.7)

结论:优化后提升的效率:164.8/101.7 = 1.62  提升:62%

7. 同时使用int优化以及多线程,效果得到进一步提升!:

	
测试条件:图片分辨率500x750
[pad] 设备:gt_p6800 双核1.4GHz
[phone] 设备:nexus s 单核1.0GHz
[优化前]-------------------------------------------
        // [pad][debug=0] 
	// [time] 14.132000 (ms)
	// [time] 15.066000 (ms)
	// [time] 25.767000 (ms)


	// [phone][debug=0]
	// [time] 175.110992 (ms)
	// [time] 196.481003 (ms)
	// [time] 113.485001 (ms)
	// [time] 162.052994 (ms)
	// [time] 178.488998 (ms) (平均=164.8)
[优化后]-------------------------------------------
	// [pad][debug=0]
	// [time] 7.036000 (ms)
	// [time] 8.646000 (ms)
	// [time] 11.264000 (ms)
	// [time] 15.105000 (ms)
	// [time] 8.559000 (ms)
	// [time] 10.316000 (ms) 平均=10.154333


	// [phone][debug=0]
	// [time] 35.727001 (ms)
	// [time] 42.995998 (ms)
	// [time] 25.725000 (ms)
	// [time] 19.454000 (ms)
	// [time] 27.643000 (ms)
	// [time] 29.118000 (ms) 平均=30.110

结论:优化后提升的效率:164.8/30.110 = 5.47  提升:447%


8. 如果追求更快的速度,可以使用neon技术,以及汇编


9. 使用查表法进行RGB <-> HSY 空间的转换,先用预处理剔除重复像素


10.  分散时间执行,不一定就是点击处理按钮的当时进行,可以开多线程在后台做一些操作,当到达这个点的时候其实大部分任务量已经计算好了


11. 缩小进行计算,然后放大进行混合计算


12. 对实时性要求高的场合,显示的时候可以先用粗质



下面是我最近工作中算法的效率

优化前期甚至

[pad] 4.5s

[phone] 25-30s


优化中期:

	// [pad] [debug=0]
	// [time] 760.336975 (ms)
	// [time] 681.442993 (ms)	//684.579669毫秒

	// [time] 623.708984 (ms)	//624.158042毫秒
	// [time] 633.445984 (ms)	//634.257毫秒
	// [time] 597.619019 (ms)	//597.847208毫秒
	// [time] 627.309998 (ms)	//627.49325毫秒
	// [time] 570.181030 (ms)	//570.961041毫秒

	// [phone] [debug=0]
	// [time] 4956.109863 (ms)	//4962.556毫秒
	// [time] 7306.398926 (ms)	//7313.900167毫秒
	// [time] 5068.775879 (ms)	//5071.079875毫秒

	// [time] 7162.111816 (ms)	//7163.000041毫秒
	// [time] 6936.270996 (ms)	//6937.797708毫秒
	// [time] 5545.577148 (ms)	//5549.424749毫秒
	// [time] 5578.754883 (ms)	//5579.310792毫秒
	// [time] 5208.092773 (ms)	//5214.470333毫秒
	// [time] 5067.886230 (ms)	//5068.809999毫秒
	// [time] 5506.863770 (ms)	//5507.421083毫秒

优化后期:

// [pad] [debug=0]
// [time] 477.792999 (ms)	//483.404708毫秒
// [time] 181.477997 (ms)	//182.157458毫秒
// [time] 179.610001 (ms)	//180.286042毫秒
// [time] 191.210007 (ms)	//191.778毫秒
// [time] 177.595001 (ms)	//179.568376毫秒
// [time] 175.041000 (ms)	//175.818125毫秒

// [phone] [debug=0]
// [time] 795.302002 (ms)	//802.008249毫秒
// [time] 1108.545044 (ms)	//1113.701375毫秒
// [time] 932.200012 (ms)	//954.378375毫秒
// [time] 939.596008 (ms)	//940.612125毫秒
// [time] 834.835022 (ms)	//835.261416毫秒
// [time] 766.768982 (ms)	//767.523749毫秒
// [time] 539.195007 (ms)	//540.059709毫秒
// [time] 561.531006 (ms)	//562.323375毫秒

可以看到,从优化中期到优化后期,[phone]几乎变快了7倍!内存的占用率也降低了很多!

@sonikk 2013-6-30 13:32:05 



研究资料:

Neon的文档:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html


Neon 原理的ppt介绍:

http://www.arm.com/files/pdf/AT_-_NEON_for_Multimedia_Applications.pdf


ARM NEON 指令(用例)

http://blog.csdn.net/tonyfield/article/details/8597549


ARM NEON Optimization. An Example

http://hilbert-space.de/?p=22


关于neon

http://blog.csdn.net/ccjjnn19890720/article/details/7291228关于neon


ARM-NEON-Intrinsics

http://www.doc88.com/p-703867169475.html


转贴ARM NEON 优化的例子

http://www.cnblogs.com/c6000/archive/2010/09/14/1826324.html


Neon 使用小结

http://blog.csdn.net/chenji001/article/details/4798754


Android NDK 之NEON优化

http://blog.csdn.net/zwcai/article/details/6843531


Android NDK(ARM开发)使用NEON优化

http://luofl1992.is-programmer.com/posts/38686.html


图像处理常用算法

http://www.rosoo.net/a/201108/14913.html


ARM首席工程师:关于Android NDK的10个技巧

http://www.chinaaet.com/article/index.aspx?id=133752


ARM(Android NDK)混编C/C++汇编优化

http://www.2cto.com/kf/201304/200755.html


Android为何要用据说效率很低的Java呢?(二)  

http://blog.163.com/liutian945@126/blog/static/16813804820111182123485/


【好文】iOS - How to convert BGRA video streams to Grayscale SUPER fast.

http://teh1337.nfshost.com/blog.py?post=198


a-very-fast-bgra-to-grayscale-conversion-on-iphone

http://computer-vision-talks.com/2011/02/a-very-fast-bgra-to-grayscale-conversion-on-iphone/


ARM NEON Optimization. An Example

http://hilbert-space.de/?p=22


Introduction to NEON on iPhone

http://wanderingcoder.net/2010/06/02/intro-neon/


Android NDK使用NEON优化,SIMD优化

http://blog.csdn.net/luofl1992/article/details/8759145


详解Android jit

http://hi.baidu.com/cool_parkour/item/570886f9d0732e14e3e3bd7f


 [置顶] Android开发性能优化简介
http://blog.csdn.net/h3c4lenovo/article/details/7669749


jni加载、卸载动态链接库文件

http://ppjava.com/?p=1273


android JNI处理图片的例子

http://blog.csdn.net/xjwangliang/article/details/7065670


bitmap.h

http://mobilepearls.com/labs/native-android-api/include/android/bitmap.h


Android c 通过skia图形库绘制文字

http://www.360doc.com/content/13/0401/16/7891085_275323215.shtml


Chrome for Mac 将换用 Skia 2D 图形库
http://www.guao.hk/tag/skia


skia源码下载:

https://code.google.com/p/skia/

http://www.chromium.org/developers/design-documents/graphics-and-skia


skia官方文档:

https://sites.google.com/site/skiadocs/


【好文】Android有效解决加载大图片时内存溢出的问题

http://www.cnblogs.com/wanqieddy/archive/2011/11/25/2263381.html


Android 读取本地(SD卡)图片

http://blog.csdn.net/knowheart/article/details/7334966


2013-7-1 0:17:59补充:


android官方ndk文档说明:

http://developer.android.com/tools/sdk/ndk/index.html#Docs


Android: NDK编程入门笔记

http://www.cnblogs.com/hibraincol/archive/2011/05/30/2063847.html


给出c&c++程序优化的几个建议,希望对你有帮助

http://blog.csdn.net/wangjiaoyu250/article/details/9185591


采用泰勒级数展开法编写的定点化的开方运算,定点化精度为Q15

http://www.pudn.com/downloads188/sourcecode/math/detail884298.html


提高专业技能之 “Codec定点化”

http://www.cnblogs.com/huaping-audio/archive/2010/07/30/1788753.html


[转]vlc android 代码编译  

http://3792615.blog.163.com/blog/static/778210942012927103347556/


package manager service是怎样选择armeabi/armeabi-v7a中的库文件的?

http://www.cnblogs.com/loveisbug/archive/2013/04/25/3042950.html


开发笔记:android ndk 开发之Application.mk

http://www.2cto.com/kf/201207/143406.html


Android NDK学习 <四> Application.mk简介

http://blog.sina.com.cn/s/blog_602f877001014ptu.html


Android NDK学习 <五> C++ 支持

http://blog.sina.com.cn/s/blog_602f877001014qe5.html


android 代码优化

http://hilary3113.iteye.com/blog/1018700


Android平台上的浮点优化

http://task.zhubajie.com/1804783/


Android开发性能优化浅论之一

http://www.cnblogs.com/huang1986513/archive/2013/03/09/2951742.html


Android arm处理器优化

http://www.360doc.com/content/13/0618/10/8204997_293662997.shtml


浮点优化选项 -ffast-math:极大地提高浮点运算速度

http://blog.csdn.net/zjujoe/article/details/2604157


Android应用程序优化都有哪些需要注意的?

http://www.mianwww.com/html/2012/05/16497.html


Android如何避免自己的应用程序被破解和反编译?

http://www.mianwww.com/html/2012/05/16500.html


Android开发过程中如何进行算法与界面的优化?

http://www.mianwww.com/html/2012/05/16495.html


Android浮点基础概念浅谈

http://developer.51cto.com/art/201001/180521.htm


ARM 浮点运算详解

http://blog.csdn.net/haomcu/article/details/7677460


CPU浮点运算与整点运算分别决定其什么方面性能?

http://www.zhihu.com/question/20086019


arm芯片中的浮点运算

http://blog.chinaunix.net/uid-27875-id-3453816.html


RTTI技术

http://baike.baidu.com/view/1042388.htm






2017-11-29 20:34:12 sinat_27860821 阅读数 1730
  • 深度学习部署系统构建

    作为SDCC系列技术峰会的一部分,来自阿里巴巴、微软、商汤科技、第四范式、微博、出门问问、菱歌科技的AI专家,将针对机器学习平台、系统架构、对话机器人、芯片、推荐系统、Keras、分布式系统、NLP等热点话题进行...

    44人学习 CSDN讲师
    免费试看
原文地址:http://blog.chinaaet.com/detail/32082.html

1、前言

    感觉很久没有写博了,最近一直在搞程序的优化,利用arm cortex-a9生成一副1024*768的图像,最初的作图时间将近1s,简直高的不能再高了,如果作一幅图都需要1s,那动画显示就是一部经典大片的名字——《Mission Impossible》。工作不能不做,所以就只能想办法把1s的作图时间缩短到1/24s,约41.7ms。面对如此头疼的事,每天尽想着改进算法和程序,也就没什么心情写博客了。

 

2、常规优化

    首先说明:如果设置了编译器优化选项,该部分的优化可能不起作用,甚至会增加程序执行时间,总的来说这部分用处不是太大,一个编译器优化选项就搞定了。如果只关心一些针对zedboard或者是zynq平台具体优化做法,可以跳过这一部分,直接看我的优化手段。

    关于这部分,网上有一些经典的文章,本文给出一个链接:

http://www.cnblogs.com/goodloop/archive/2010/10/02/1841171.html

    链接中的文章,提到的优化策略有很多,我简单列出几点:

i)   利用移位代替乘/除2的操作,利用移位加法代替乘法;

ii)  优化循环终止条件:使用减计数代替惯用的加计数,使用i!=0作循环终止条件;

iii)  使用合适的变量类型:处理器字长为32bits,所以使用8bits、16bits数据类型会增加处理难度;

 

    除此之外,我想说一下ARM官方编程手册《Cortex-A Series Programmer's Guide》里提到的优化方法:

i)、使用inline函数

    inline函数主要节省了调用函数所占用的时间。这里需要注意的是GCC对inline函数的处理方式:inline关键字仅在同一个编译单元内有效,也就是说,如果想要在a.c里调用b.c里定义的inline函数需要做特殊处理,关于这个本文不多做说明,详细情况请google或者百度一下。

ii)、充分利用已经得出计算结果的变量

    这种说法听起来比较拗口,还是举例说明比较清晰:

    ---------------------代码开始------------------------

    i = a * b + c;
    j = a * b * d;

    ---------------------代码结束------------------------

    可以优化为:

    ---------------------代码开始------------------------

    tmp = a * b;
    i = tmp + c;
    j = tmp * d;

    ---------------------代码结束------------------------

iii)、避免循环

    例如,需要进行小批量数据的赋值时,可以不使用循环,例如:

    ---------------------代码开始------------------------

    for (i = 0; i < 10; i++)
    {
        x[i] = i;
    }

    ---------------------代码结束------------------------

    可以优化为:

    ---------------------代码开始------------------------

    x[0] = 0;
    x[1] = 1;
    x[2] = 2;

    x[3] = 3;
    x[4] = 4;
    x[5] = 5;
    x[6] = 6;
    x[7] = 7;
    x[8] = 8;
    x[9] = 9;

    ---------------------代码结束------------------------

 iv)、结构对齐、循环优化、变量选择等

    这也属于常规优化,我就不细说了,可以参考上文提供的链接,或者自行搜索。

~to be continued

3、几种对我有所帮助的最重要的优化手段

i)、对于频繁使用的函数,不要给它穿太多衣服。

    还是举例说比较清晰:

    起初我使用显示IP核提供的驱动程序,其中像素描绘函数drawPixel()是这样实现的:drawPixel()调用了函数B(),函数B()又调用了函数C(),最后通过函数指针对C()进行一次赋值调用,在这一层才真正实现对DDR3操作。这种写法想必有其优点,如:可以实现对函数的动态赋值,对底层函数进行了较好的封装等。

    像素描绘函数实质上就是向显存某个地址写入颜色数据,因此,我考虑自己编写像素操作函数,我的drawPixel()不需要穿那么多衣服,一件足以——直接操作DDR3的与像素坐标对应的存储地址。这样以来,减少了函数调用的开销,避免了函数指针的使用,势必将节省大量时间,事实正是如此:由于ARM程序的主要功能就是生成显示画面,所以drawPixel()的使用相当频繁,对drawPixel()作出改进之后,大大节省了作图时间,从最开始的980ms,减少到560ms。

ii)、SDK中设置编译器优化选项。

    设置方法如下:

    a)、菜单栏选择project菜单:

    b)、选择属性选项,即Properties,弹出对话框如下:

    c)、在C/C++ Build选项下,选择Setting,在右边的Tools Setting选项卡下,找到ARM gcc compiler,在该栏下选择Optimization,右边就可以看到优化等级(Optimization Level)选项,各个选项在下图中用红色圆圈标出:

    d)、根据需要设置优化选项:

    这里说一下,优化选项的意义:

    优化命令是由连接符-加上大写英文字母O后面紧跟阿拉伯数字(0、1、2、3、time、space)组成的,刚开始我在makefile相关文件设置优化选项,由于在看资料时,我把英文字符O看成数字0了,导致了一些错误。

-Ospace:命令编译器尽可能地减小代码尺寸,可以以牺牲代码执行效率为代价

-Otime:命令编译器尽可能地提高执行速度,可以以牺牲代码尺寸为代价;

-O0:最低等级的优化选项,关闭绝大多数优化,提供全面的调试信息

-O1:去除无用的静态函数和内联函数;避免严重影响调试信息的优化;在代码密度和调试信息之间寻求较好的平衡;

-O2:高级优化;有些调试信息可能无法看到,经过优化后的代码和源码差异较大;

-O3:和-O2一样,但是可以设置对代码尺寸和执行时间有所侧重的优化。-O3 -Otime执行速度快于 -O2 -Otime,但代码尺寸有所增加;-O3 -Ospace代码尺寸小于 -O2 -Ospace,但执行速度可能要慢一些。

 

    设置完编译器优化选项之后,代码执行效率又有了较大幅度地提高,从560ms减少到330ms左右。

    这里需要指出的是:上文提到的常规优化甚至是对一些算法的优化,在编译器优化前后的效果可能是相反的,换句话说,设置编译器优化选项之前,常规优化和算法优化一般都是有效的。但是,设置编译器优化选项后,之前所做的优化有可能不起作用,甚至起反作用。这里还是比较纠结的,造成这种现象的原因可能是对一些算法的经典改进本质上还是实现了常规优化算法,而编译器优化时会自动实现常规优化。举个例子:设计直线绘制函数时,考虑利用直线的对称性,在一次循环体内绘制两个像素。开编译器优化前,效率得到提高;开编译器优化后,效率反而降低。

iii)、使用缓存

    优化到330ms之后,在这里停了好长时间,有一个填充算法耗时比较长,大概占200ms的样子,但是这个填充算法我一直没想好怎么设计,也就暂时用一个简单粗暴但耗时的算法替代一下,尽管如此,不考虑填充运算,仍然离目标很远。这时候和师傅聊天,他提到之前使用DSP碰到的缓存问题,我突然想起来,每次初始化内核的时候,貌似都把缓存关掉了。跑去实验室一看,果然如此,开启缓存之后,又是一个惊喜:330ms变成了33ms。但是画面存在一些问题:画面出现了很多黑点,也就是说,本来应该填充的像素没有成功填充。

    查阅了arm cache相关资料后,怀疑和cache工作模式有关系,cache默认工作模式是write-back模式,而不是write-through。关于chace的工作模式,本文不详细叙述了,大概说一下:write-back模式下,cache和外存不是同步更新的;write-through模式下,在操作cache的同时,同步操作外存,这样以来,速度较write-back模式要慢一些。了解了二者差别之后,就知道对于我的项目而言,必须要保证帧存的成功写入,所以要将cache工作模式设置为write-through。查阅了ug585,arm cortex-a series programmer guide以及arm architecture reference manual发现cortex-a9的L1缓存工作模式是固定的,无法更改,L2缓存工作模式可以更改。

    查阅ug585,研究与L2缓存相关寄存器,发现偏移地址为0x00000F40的reg15_debug_ctrl寄存器,可以控制L2缓存的工作模式。reg15_debug_ctrl寄存器定义如下:

    在初始化L2 cache时,将reg15_debug_ctrl bit1(DWB)置1,就可以将L2 cache设置为write-through模式了。

    将L2 cache设为write-through模式,再将L1指令缓存打开,将L1数据缓存关闭,屏幕坏点就消失了。但是由于关闭了L1数据缓存,作图时间也提高到了49ms左右,其中填充算法占了44ms左右,由此可见我的那个填充算法不靠谱,最起码改进空间很大。

iv)、填充算法的改进

    这个算法的改进主要涉及计算机图形学的知识,我不讲算法,只将和优化相关的一些思想。起初我使用的是边标志填充算法,该算法主要有3个步骤:

        a)、进行标志填充,将边界和一些分界线填充相应的标志颜色;

        b)、逐行或逐列扫描充范围内每个像素,读取该像素的颜色值,判断该像素是否是标志;

        c)、根据读取到的标志决定该像素的填充颜色;

    这个算法主要有以下缺陷:

        a)、对同一个像素进行反复读写操作,由于像素的颜色信息,存在外存中,访问时间相对较长,所以会影响效率。

        b)、在扫描转换时,需要双层循环,众所周知循环的嵌套对效率的影响也是很大的。

    针对上述缺陷,我放弃了边标志填充算法,使用了一维数组将边界的纵坐标保存起来,逐行扫描时读取对应纵坐标的值,对于不固定的填充边界,在扫描过程中实时计算,这样以来,通过数组保存边界信息使得双层循环变为单层循环;与此同时,对同一个像素的操作也只进行一次写操作。

    由于是评估A9的作图性能,项目也没有具体要求,师傅说50ms也还可以了,主任说是要控制在33ms之内,也就是一秒钟至少要显示30幅画面。我也是想做到主任的要求,做了算法改进后,觉得能进30ms就满意了,没想到新的算法,将填充时间控制在个位数……这样一来总的作图时间控制在10ms左右,也算是一个惊喜吧~

4、结束语

    总的来说,Cortex-A9的程序优化不是很难,但是编译器自动优化带来的不可控性,给优化工作带了一定的挑战——通过实际运行才知道所做的优化是否有效。但所有的优化手段还是离不开最常用的优化方法,所以我们在平时编码的时候,养成良好的编码习惯,多编写编译器“喜欢”的代码,将优化方法融入自己的编程风格中,开发必将事半功倍!

2019-12-16 21:19:54 qq_38410730 阅读数 3279
  • 深度学习部署系统构建

    作为SDCC系列技术峰会的一部分,来自阿里巴巴、微软、商汤科技、第四范式、微博、出门问问、菱歌科技的AI专家,将针对机器学习平台、系统架构、对话机器人、芯片、推荐系统、Keras、分布式系统、NLP等热点话题进行...

    44人学习 CSDN讲师
    免费试看

当在ARM芯片上进行一些例如图像处理等计算的时候,常常会因为计算量太大造成计算帧率较低的情况。因而,需要选择一种更加简单快捷的计算方式以获得处理速度上的提升。ARM NEON就是一个不错的选择。

本文实例源码github地址https://github.com/yngzMiao/yngzmiao-blogs/tree/master/2019Q4/20191216


Neon指令优化

NEON是一种SIMD(Single Instruction Multiple Data)指令,也就是说,NEON可以把若干源(source)操作数(operand)打包放到一个源寄存器中,对他们执行相同的操作,产生若干目的(dest)操作数,这种方式也叫向量化(vectorization)

可能你对这个描述还不够清晰,简单来说,就是:NEON指令优化的精髓就在于同时在不同通道内进行并行运算。通常可用于图像等矩阵数据的循环优化

更简单的说,就是,将Neon寄存器分为多个通道,每个通道存储一个数据。一条对Neon寄存器的计算指令,实际上,是对各通道的数据分别的计算指令。即寄存器位宽,直接影响到数据的通道数。

例如:在ARMv7的NEON unit中,register file总大小是1024-bit,可以划分为16个128-bit的Q-register(Quadword register)或者32个64-bit的D-register(Dualword register),也就是说,最长的寄存器位宽是128-bit。那么,假设我们采用32-bit单精度浮点数float来做浮点运算,那么可以把最多128/32=4个浮点数打包放到Q-register中做运算,即4个4个参与计算,从而提高吞吐量,减少loop次数。


Neon指令的使用

主流支持目标平台为ARM CPU的编译器基本都支持NEON指令。可以通过在代码中嵌入NEON汇编来使用NEON,但是更加常见的方式是通过类似C函数的NEON Instrinsic来编写NEON代码。本文统一采用后者。

硬件平台

本文的例子都是基于ARMV7架构平台。ARMV7架构包含:

  • 16个通用寄存器(32bit),R0-R15
  • 16个NEON寄存器(128bit),Q0-Q15(同时也可以被视为32个64bit的寄存器,D0-D31)
  • 16个VFP寄存器(32bit),S0-S15
    其中:NEON和VFP的区别在于VFP是加速浮点计算的硬件不具备数据并行能力,同时VFP更尽兴双精度浮点数(double)的计算,NEON只有单精度浮点计算能力

头文件和编译选项

在使用NEON Instrinsic来进行编写NEON代码前,需要引入头文件:

#include <arm_neon.h>

同时,在编译的时候,需要指定编译参数。如果使用CMakeLists.txt,可以指定:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon")

关于编译选项,可以参考:ARM平台NEON指令的编译和优化

NEON Instrinsic详细解释

数据类型

对于数据类型的命名,一般遵循这样的规则:

<基本类型>x<lane个数>x<向量个数>_t

其中,向量个数如果省略表示只有一个。

基本类型int8,int16,int32,int64,uint8,uint16,uint32,uint64,float16,float32

lane个数表示并行处理的基本类型数据的个数。

按照上述的规则,比如:

float32x4_t 

指令函数

对于指令函数的命名,一般遵循这样的规则:

v<指令名>[后缀]_<数据基本类型简写>

其中,后缀如果没有,表示64位并行;如果后缀是q,表示128位并行;如果后缀是l,表示长指令,输出数据的基本类型位数是输入的2倍;如果后缀是n,表示窄指令,输出数据的基本类型位数是输入的一半

数据基本类型简写:s8,s16,s32,s64,u8,u16,u32,u64,f16,f32。

按照上述的规则,比如:

vadd_u16:两个uint16x4相加为一个uint16x4
vaddq_u16:两个uint16x8相加为一个uint16x8
vaddl_u16:两个uint8x8相加为一个uint16x8

指令名

Neon的指令名主要分为:算术和位运算指令、数据移动指令、访存指令
算术和位运算指令最简单,包括add(加法),sub(减法),mul(乘法)这些基本指令。

实际编程中经常要在不同NEON数据类型间转移数据,有时还要按lane来get/set向量值,NEON intrinsics也提供了这类操作。

  • dup[后缀]n<数据基本类型简写>:用同一个标量值初始化一个向量全部的lane;
  • set[后缀]lane<数据基本类型简写>:对指定的一个lane进行设置
  • get[后缀]lane<数据基本类型简写>:获取指定的一个lane的值
  • mov[后缀]_<数据基本类型简写>:数据间移动

NEON访存指令可以将内存读到NEON数据类型中去,或者将NEON数据类型写进内存。可以支持一次读写多向量数据类型。

  • ld<向量数>[后缀]_<数据基本类型简写>:读内存
  • st<向量数>[后缀]_<数据基本类型简写>:写内存

实例

实例内容:对于1280 * 720 * 3的图片数据,需要对每个像素点进行同样的加法和乘法运算,比较非Neon和Neon两种方式的耗时。
源码:

# include <iostream>
# include <chrono>
# include <random>
#include <arm_neon.h>

int main(int argc, char const *argv[])
{
  float *data_tmp = new float[1080 * 720 * 3];
  std::default_random_engine e;
  std::uniform_real_distribution<float> u(0, 255);
  for(int i = 0; i < 1080 * 720 * 3; ++i) {
    *(data_tmp + i) = u(e);
  }

  float *data = data_tmp;
  float *data_res1 = new float[1080 * 720 * 3];

  std::chrono::microseconds start_time = std::chrono::duration_cast<std::chrono::microseconds>(
    std::chrono::system_clock::now().time_since_epoch()
  );

  for(int i = 0; i < 1080 * 720 * 3; ++i) {
    *data_res1 = ((*data) + 3.4 ) / 3.1;
    ++data_res1;
    ++data;
  }

  std::chrono::microseconds end_time = std::chrono::duration_cast<std::chrono::microseconds>(
    std::chrono::system_clock::now().time_since_epoch()
  );

  std::cout << "cost total time : " << (end_time - start_time).count() << " microseconds  -- common method" << std::endl;

  data = data_tmp;
  float *data_res2 = new float[1080 * 720 * 3];

  start_time = std::chrono::duration_cast<std::chrono::microseconds>(
    std::chrono::system_clock::now().time_since_epoch()
  );

  float32x4_t A = vdupq_n_f32(3.4);
  float32x4_t B = vdupq_n_f32(3.1);
  for(int i = 0; i < 1080 * 720 * 3 / 4; ++i) {
    float32x4_t C = (float32x4_t){*data, *(data + 1), *(data + 2), *(data + 3)};
    float32x4_t D = vmulq_f32(vaddq_f32(C, A), B);
    vst1q_f32(data_res2, D);
    data = data + 4;
    data_res2 = data_res2 + 4;
  }

  end_time = std::chrono::duration_cast<std::chrono::microseconds>(
    std::chrono::system_clock::now().time_since_epoch()
  );

  std::cout << "cost total time : " << (end_time - start_time).count() << " microseconds  -- neon method" << std::endl;

  return 0;
}

编写CMakeLists.txt,用于项目编译:

cmake_minimum_required(VERSION 3.0)
project(main)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon")
add_definitions("-Wall -g")

add_executable(${PROJECT_NAME} main.cpp )

install(TARGETS ${PROJECT_NAME}
  RUNTIME DESTINATION ${PROJECT_SOURCE_DIR})

在同级目录下编写main.sh,进行项目编译:

#/bin/bash

export ANDROID_NDK=/opt/env/android-ndk-r14b

rm -r build
mkdir build && cd build 

cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
	-DANDROID_ABI="armeabi-v7a" \
	-DANDROID_PLATFORM=android-22 \
	..

make && make install

cd ..

将生成的可执行文件main,push到设备端进行运行,最终的运行结果:

cost total time : 112538 microseconds  -- common method
cost total time : 44217 microseconds  -- neon method

可以看出,使用Neon指令集优化,省下了近60.71%的运行时间。


相关阅读

2019-06-22 20:50:52 YEYUANGEN 阅读数 1811
  • 深度学习部署系统构建

    作为SDCC系列技术峰会的一部分,来自阿里巴巴、微软、商汤科技、第四范式、微博、出门问问、菱歌科技的AI专家,将针对机器学习平台、系统架构、对话机器人、芯片、推荐系统、Keras、分布式系统、NLP等热点话题进行...

    44人学习 CSDN讲师
    免费试看

1. 核心板简介

SOM-XM5728 是信迈科技基于 TI AM5728 处理器的工业级核心板,采用沉金无铅、十层板、工业级设计,可应用于运动控制系统、机器视觉处理系统、图像识别系统、音频分析系统、视频分析系统,高端测量仪器等行业应用。

  • 客户可基于不同的应用,采用不同的 CPU、内存、外存配置。核心板支持基于 TI AM5728处理器,内置两个 ARM Cortex-A15 处理器,和两个浮点 DSP C66x™ Floating-Point VLIW DSP@750MHz、同时支持多种工业协议的 PRU-ICSS。内存支持工业级 1GB-4GB DDR3,外存则支持工业级 4GB-16GB eMMC,扩展非常方便。
  • SOM-XM5728 核心板支持 EtherCAT,EtherNet/IP、PROFIBUS等多种工业通信协议,产品可轻松实现各总线控制和工业组网;通过双 C66x DSP 实现更复杂的,更高精度的运动控制插补算法;并可通过 GPMC 总线连接 FPGA 的方案,实现更多的工业控制与图像处理功能。
  • SOM-XM5728核心板通过 8x McASP 和双 C66x DSP,可实现 16 路或 32 路高端应用的音频处理分析算法,可实现声源定位、回音消除等各种音频分析处理场景应用。
  • 视频编解码能力强大,支持 1 路 1080P60 或 2 路 720P60 或 4 路 720P30 视频硬件编解码,支持 H.265 视频软解码;在视频处理上,利用双 C66x DSP 的强大功能,可实现复杂的图像识别、视频分析及识别、各种数据实时分析处理、以及其他高端 AI 应用场合等。
  • 高性能 GPU,双核 SGX544 3D 加速器和 GC320 2D 图形加速引擎,支持 OpenGL ES2.0;
  • SOM-XM5728软件 SDK 开发包,提供 Linux4.14.67 系统、DSP RTOS 系统,支持 OpenCL、OpenMP、SysLink IPC 多核开发提供 ARM 与 DSP 多核间通信方法与案例,让高速信号处理、音视频数据分析等开发难度大大降低。
  • 体积小,大小仅 86mm*60.5mm;

 

评估板:

2  AM572x 处理器规格

处理器

AM5728

AM5726

ARM

2x Cortex-A15@1.5GHz

2x Cortex-A15@1.5GHz

DSP

2x C66x@750MHz

2x C66x@750MHz

图像加速器

2x 3D and 1x 2D

N/A

显示输出最高分辨率

1080p@60fps

N/A

显示输出接口

3x LCD and 1x HDMI 1.4a

N/A

 

3. 主要特点

同时具有 2x Cortex-A15@1.5GHz、2x C66x DSP@750MHz、2x Cortex-M4、2x PRU 

采用工业级精密BTB 连接器进行连接,保证信号完整性,安装拆卸方便稳定可靠

最大支持 6 路视频输入3路视频输出

最大支持 8  McASP

最大支持 2  CAN 接口

最大支持 10  UART

最大支持 3  LCD

最大支持 4  SPI

最大支持 3  MMC

最大支持 5  I2C

最大支持 2 路千兆网口

支持 1  SATA-2 接口

支持 1  USB 3.0

支持 2 USB 2.0

支持 1  HDMI

支持 1  PCIe 3.0, 支持 1*2-lanes  2*1-lane 两种模式

支持 Linux 4.14.67、DSP RTOS 系统,支持实时 RT-Linux 系统

工作温度范围:-40℃~85℃(工业级)

 

4. 应用领域

本核心板应用广泛,主要包括工业运动控制系统、机器视觉处理系统、音频分析系统、高清图像识别系统、视频分析处理系统、医疗图像处理、电力自动化、高端测量仪器、EtherCAT 主/从控制器等应用领域。

 

核心板规格

1. 核心板结构

                                                                                        机械尺寸图

结构参数

连接器

BTB 连接器

尺寸

86MM*60.5MM*6MM

引脚间距 

0.5MM

引脚数量 

400(80+80+80+80+80)

核心板和底板间合高 

5MM

安装孔数量

6个

 


硬件规格参数

项目

类型

型号参数

说明

核心配置

CPU

TI AM572x

ARM Cortex-A15 CPU@1.5GHz ,集成C66x  DSP@750MHZ, 集成 SGX544  3D GPU

片上缓存

L2 Cache

ARM Cortex-A15:1Mbyte
C66x:288KB

On-Chip Memory

2.5 MByte

内存

DDR3

1GB/2GB/4GB

eMMC

4 -16GB eMMC

电源管理

TPS6590377ZWSR

总共有15路输出,有输出短路和过载保护,有过温警告和关断保护,有过压和欠压保护,可通过 I2C 接口控制

连接器

5 x 80 pin B2B 连接器

包含一个高速连接器。引脚间距 0.5MM,共 400PIN

引脚功能

显示输出

3 x LCD

支持 24bit、16bit、8bit,与其他接口复用

1 x HDMI

支持 HDMI 1.4a

Video

6 x Video Input Ports

3 x Video Input Ports

最大支持 6 路视频输入3路视频输出

以太网

2 x Ethernet

支持 MII、RMII、RGMII,与其他接口复用

PRU

2 x PRU-ICSS

支持PRU-ICSS

串口

10 x UART

3.3V TTL 电平,UART3 为调试串口,10路均支持硬流控和软流控,与其他接口复用

音频

8 x McASP

支持 I2S 和 S/PDIF,支持录放音,与其他接口复用

数字视频输入

10 x VIN

支持 24bit、16bit、8bit,与其他接口复用

MMC

3 x SDIO

支持 1bit 和 4bit,与其他接口复用

USB

2 x USB

其中一路支持 USB3.0, 一路支持USB 2.0

PCIe

1 x PCIe 3.0

支持 1*2-lane 或 2*1-lane,需要外部输入时钟

SATA

1 x SATA

SATA Gen2

PWM

3 x PWM

与其他接口复用

I2C

5 x I2C

其中 IIC1 配置 PMIC,与其他接口复用

SPI

4xSPI

与其他接口复用

DCAN

2x DCAN

支持 2.0B 协议,与其他接口复用

GPMC

1x GPMC

16 位地址线,与其他接口复用

JTAG

1 x JTAG

JTAG用于在线调试

TIMER

16 x Timers

最大支持16个定时器

EMIF

2 x EMIF  

 

按键控制器

1 x Keyboard Controler

支持 9X9 矩阵键盘,与其他接口复用

RTC 时钟

独立 RTC

纽扣电池供电,可关断系统电源时保存时间

电源

电源输入

DC 5V/3A

 

2. 核心板工作环境

环境参数

最小值

典型值

最大值

工业级温度

-40℃

/

85℃

工作电压

4.3V

5V

5.25V

3. 核心板功耗

电压

电流

功耗

4.93V

830mA

4.09W

备注:功耗测试基于深圳信迈 XM-EVM572x 开发板进行。

4 软件参数

Linux

启动下载

版本

u-boot 2018.01

启动方式

支持从 eMMC/SD 启动

启动速度

支持启动速度优化

启动画面

支持开机画面定制

下载方式

支持串口和网络下载

Kernel

内核版本

Linux 4.14.67

支持文件系统

EXT3/EXT4/NFS/JFFS2/UBIFS

设备驱动

提供所有外设驱动

文件系统

Rootfs

可根据需求进行文件系统定制

图形界面

QT5.7

提供完善的QT开发资源

DSP

CCS 7.1

 

DSP 调试及开发工具,提供安装、基本使用指南

RTOS SDK

 

提供安装、基本使用指南

IPC通讯协议

 

提供 ARM 与 DSP 之间多核通讯协议资料

DSP Sysbios

 

提供 DSP RTOS 系统镜像

 

技术服务

1. 开发资料

提供核心板引脚定义、可编辑底板原理图、可编辑底板PCB,缩短硬件设计周期;

提供系统烧写镜像、内核驱动源码、文件系统源码,以及丰富的 Demo 程序;

提供完整的平台开发包、入门教程,节省软件整理时间,上手容易;

 提供基于核心板的产品设计手册,包括硬件设计、系统软件移植、驱动开发、应用软件开发环境。产品开发速度大幅提升,开发周期缩短一半以上,最快一周出产品设计!

提供主芯片数据手册、软件开发 SDK 包、软件开发环境,让您快速进入软件开发状态;

提供 QT 开发环境,快速进入应用软件开发;

 

2. 技术支持服务

  1. 协助底板设计和测试,减少硬件设计失误;
  2. 判定产品是否存在故障
  3. 信迈嵌入式产品的软硬件资源解释
  4. 帮助您正确地编译与运行我们提供的源代码
  5. 按我们提供的产品文档,操作信迈嵌入式软硬件产品出现异常问题的
  6. 提供长期可靠的售后支持与服务

 

3. 增值服务

  • 底板/核心板定制设计
  • 项目合作开发
  • Linux系统定制
  • 嵌入式软件开发

 

4. 更多帮助

销售邮箱:sales@szxinma.com

技术邮箱:support@szxinmai.com

信迈官网:www.szxinmai.com

淘宝商城:https://item.taobao.com/item.htm?spm=a230r.1.14.70.7e7c26f2FHlpA0&id=596565683992&ns=1&abbucket=12#detail

TI 中文论坛:http://www.deyisupport.com/

TI 英文论坛:http://e2e.ti.com/

TI 官网:www.ti.com

2017-08-26 16:04:11 STN_LCD 阅读数 3895
  • 深度学习部署系统构建

    作为SDCC系列技术峰会的一部分,来自阿里巴巴、微软、商汤科技、第四范式、微博、出门问问、菱歌科技的AI专家,将针对机器学习平台、系统架构、对话机器人、芯片、推荐系统、Keras、分布式系统、NLP等热点话题进行...

    44人学习 CSDN讲师
    免费试看

http://blog.sina.com.cn/s/blog_1368ebb6d0102vuhg.html


嵌入式图像处理算法优化指南,适用于任何基于ARM或PC平台的视觉应用程序开发

转载2015-08-05 15:09:15

第一章 绪论

1.1 

计算机视觉/图像处理研究者的困惑

作为一名从业近10年、一直致力于视频目标识别和跟踪的研究者,笔者认为视频图像处理的应用远远没有达到其应有的程度。究其原因,可能包括如下几个方面:

n  科研单位和高校的研究者多以理论研究为主,偏论文的评价体系使得研究者更关注理论是否新颖,忽略了算法效率及实用性,造成绝大多数的研究成果在实际应用中无法应用或成本太高。

n  Mathlab等工具和OpenCV等库函数加快了研究者出论文的效率,并加快了功能初步验证的过程,但其带来的负面影响不容小觑。由于对现有工具产生了很强的依赖,缺少对图像处理技术本质的认识及经验积累,需要新的功能时无法高效实现。

n  图像处理本身是一门工程技术,在一定程度上不具有“可推导性”和“通用性”,要想解决某一问题,必须针对性地提出解决方案才有可能研制出高效的功能模块。

n  硬件平台的依赖性太强,研制成本高。之前的图像处理算法通常运行于DSP或FPGA平台上,硬件研制和移植成本动辄上百万,使得普通开发者望而却步。

1.2 

让嵌入式图像处理技术流行起来

为加快图像处理技术的工程应用,“让嵌入式图像处理技术流行起来”,嵌视科技倡导开发者遵循如下两个原则:

n  除可购置的功能芯片外,针对特定应用设计针对性的解决方案,并自己编写所有的算法,保证整个模块的高效运行。在这个问题上周围经常有反对的声音,说自己实现的算法肯定没有成熟的库函数的快。在单个标准的功能函数方面,我们承认库函数在效率方面并不低,但结果往往是笔者开发的应用模块通常比调用库函数的快很多倍。比如,在目标跟踪的应用中,笔者开发的模块在双核2.4G

的PC上耗时只有

0.3ms

,事后分析发现所有代码没有用到任何一个“完整”的库函数(在此基础上开发了

QS-PTE9

中的“视觉跟踪示例程序”)。

“磨刀不误砍柴工”,只有熟练掌握图像处理的实现技巧,灵活设计应用模块的针对性实现方案,才能开发出高效稳定的嵌入式视觉应用模块,本手册将通过实例的形式带你进入这条路线。当然我们只是领进门,修行还得靠自身

n  选用基于ARM的嵌入式图像处理平台,以节省硬件及移植成本。原因在1.3节中介绍。

为节省彼此的时间,本手册推崇言简意赅的原则,希望您能理解。第二章 高效视频图像处理的几个基本原则

高效视频应用模块的开发更多需要的是长时间的经验积累,在这里我们仅仅列出一些原则性的经验:

  • 算法级的优化或简化永远放到第一位:算法的计算复杂度决定了经过优化后能够达到的最佳性能,没有好的算法作为基础,一切优化都是白搭。

  • 代码优化是必不可少的

:同一个算法,不同的人实现出来效率方面可能存在10

倍以上的差距,消除这10倍以上的差距是开发嵌入式视觉应用模块必须掌握的基本技能。

算法级的优化属于算法设计的问题,是图像处理研究的重点,不同应用差别很大,本手册不作介绍。

这里列出图像处理方面代码优化的几个基本原则:

  • 重点优化针对每个像素的操作,即for

循环里面的:理由再简单不过了,假设我们要处理的图像是30万像素的,每个for循环减少10次运算就减少了300万次运算,那效率方面就会提高很多。

  • 不要在大的for

循环里面调用函数:调用一次函数就涉及到函数寻址、参数传递过程,如果对每个像素调用一次函数想想会浪费多少时间啊。

  • 避免大量的除法运算

:不管是PC

,还是ARM、DSP等,除法运算都很慢,尽量以乘法和移位来替代。

  • 查表是实现高效运算的首选

:尽可能多的利用查表来代替计算。

  • 避免重复计算

  • 尽量采用定点整形运算,尽量避免大量的多层级的if…else…

:使得开发的算法能够支持更多的嵌入式图像处理开发平台。

是不是很枯燥和无从下手?后面所有的章节将以实例的形式一步一步教会你如何开发高效的图像处理算法。