代码拖拉组件生成html代码化如何有效移动端功能测试

吐槽前端组件化的踩坑之路原创
亚里士朱德
这篇文章分享的不是成功的经验,而是失败的教训~
&!-- more --&
关于为什么要研究组件化以及之前对组件化实现方式的理解都在这篇文章——《》。本来按照之前的思路,觉得组件化应该有三种实现方式,一种是后端模板;一种是浏览器端由js实现,包括reactjs的组件、angular的指令等,不过这些对css文件无法管理(有个插件号称完美实现组件化,研究完之后再分析);最后一种就是利用构建工具实现组件化。
如果真能找到这样一种构建工具,不依赖前后端语言、模板、框架,在构建代码的时候直接直接将组件打包是不是很完美?如果你也这么想,那么恭喜你可以跟我一其踏上一段踩坑之旅了。
目标已经明确的话开始寻找工具。理想的是有工具插件直接实现组件化,差一点的话自己稍加改造实现也可以接受。看看现在比较流行的工程化工具:
首先研究这个最新最火的工具,一进入官网就被那个炫酷的css3立方体吸引了,看上去很高大上的样子。官网上内容很多,虽然是英文的但是问题不大。看到菜单上有一系列教程(list of tutorials)非常欣喜,心想好软件就是不一样,教程都写得这么多。一点开傻眼了,根本就不是什么学习教程,就是各种语言凑起来的文章,完全无法引导新手很好的学习,也没有分类。照着例子使用了之后发现如其所说只是个模块打包工具,恨不能让任何页面只引用一个js一个css,对第三方依赖的处理也是狗血,要么合并成一个,要么一个一个配置,手动在html中维护,而且还是侵入式的改变源代码内容。功能很简单,实现过程很复杂,蛋疼之后更是伴随一阵心疼,遂放弃。
如有不对之处,欢迎webpack资深玩家拍砖指点。
其实从fis刚出来的时候我就在关注fis,那时候因为觉得插件不够丰富,再加上项目中使用的是grunt,所以放弃了。这次看到fis的教学视频和fis3的时候我是内心有些激动的。一方面见其生机勃勃,另一方面介绍了百度产品实现组件化的经验。
事情真的那么完美吗?首先不得不承认fis3是一个比较成熟的构建工具,但是一上手就坑了我,release发布代码的时候不能清除目录,只能覆盖发布,号称400多个插件中也没找到可以实现的,我只能用一个字形容——囧。这种感觉就像你来到了一栋摩天大楼,但是它没有电梯,你只能自己爬上去。再细致研究发现其组件化也是依赖后端语言实现的,和后端模板集成在一起,做事情做一半,真是无语。至于Angular和Angular2这种靠前端模板的例子也不是我要找的答案。
不过其目录划分可能还有一些借鉴意义吧。
gulp和grunt功能上差不多,丰富的扩展性决定了其能成为最强大的前端构建工具。gulp效率高一些,所以这里只讨论gulp。当不停地寻找合适插件的时候终于发现一个关键性的功能似乎不能实现,那就是组件的嵌套引用以及依赖资源的自动合并,如果需要实现这个功能那么要动态处理html代码识别资源然后进行整合,这个功能是不是有些熟悉,对,这就是之前写过的利用后端模板引擎做的事情。
想到这里,这个坑就明显了:我在试图用构建工具来侵入代码来完成模板引擎该做的工作而同时它还无法像模板引擎一样填充数据。这就好比我在用羽毛球拍打乒乓球,还一直觉得是球拍品牌不够好所以打不好球。
回过头来看看构建工具的职能到底是什么?
fis3给其定义了三大职能
资源定位:获取任何开发中所使用资源的线上路径;
内容嵌入:把一个文件的内容(文本)或者 base64 编码(图片)嵌入到另一个文件中;
依赖声明:在一个文本文件内标记对其他资源的依赖关系;——很可惜这个任务没有完全完成
这三大职能看似很完美,但实际上都是需要在修改源代码的基础上实现,这种耦合程度就很不友好。一方面造成代码混乱,另一方面如果要替换构建工具也变得不可能。
再看gulp/grunt这种自动化构建工具,将压缩、编译、单元测试、lint等重复性工作自动化,不要求改变源码,我觉得这种无耦合的方式才通用更利于维护。
总之,如果编写fis3插件来自动处理依赖声明的话,利用构建工具来实现组件化应该是可以的。只是在前后端分离、行为结构样式分离的今天来做这样一件事显然不是最佳最友好的实现方式~
随时随地看视频组件化软件安全性测试集成环境研究与开发_ kk_百度文库
您的浏览器Javascript被禁用,需开启后体验完整功能,
享专业文档下载特权
&赠共享文档下载特权
&10W篇文档免费专享
&每天抽奖多种福利
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
组件化软件安全性测试集成环境研究与开发_ kk
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩2页未读,
定制HR最喜欢的简历
你可能喜欢软件开发的可持续性和打造软件快速反应部队
一个小公司怎么实现APP的UI自动化测试
作为一个在软件和互联网行业浸润了20年的老兵,我接触了很多公司,也面试了很多人,发现国内现在能实现APP的UI自动化测试的公司很少,我的一个之前在淘宝测试团队工作的同事也讲,在淘宝也是部分实现了自动化测试,因为很多业务变化很快,实现自动化测试意义不大。我面试过的很多测试人员,有些是工作了快10年的也是这种观点。我们公司是一个创业型的汽车后市场O2O企业,因为老板的平台化战略,我们的APP非常庞大,安卓我们有5个APP,IOS我们有4个APP,功能覆盖洗车,保养,维修,拼车,租车,代驾,交友朋友圈等等,功能复杂度堪比淘宝,也不是我这里吹牛,大家下载看看就知道了,在应用宝搜索”逸休联盟”就可以看到了。
因为我们基于精兵战略,我们的开发和测试人数一直都很少,安卓和IOS开发巅峰时候也就是各自5个人,现在缩减到各自3人,测试人员从之前的2人变成了1人。因为是个创业公司,我们的业务现在还是非常不稳定,功能经常是变来变去,这也是很多创业公司遇到的,但我们还有2个困难,功能庞大,人员少,根据我的了解很多创业公司的功能比我们少很多,但人员多很多,提升开发效率和测试效率就变成了我们需要解决的问题。今天这里,我主要谈谈我们怎么样基于现在流行的Appium解决自动化测试的问题。
上面是我们APP的架构图,安卓和IOS都是相同的,独立组件层是一个重用性非常高的模块,只依赖安卓和IOS自身类库,和知名的类库,如百度地图等,这个模块可以脱离我们APP使用。在这个层上面,网络层是和后台进行交互的,一切数据请求包括图片都通过这个层面,我们学习了几个开源代码基础上面独自开发的,这样做的考虑有很多,这里不在多讲了,如果有兴趣,请关注我后面的文章。数据模型层用于和后台的数据交互,也同时用于APP界面的VIEW的交互。项目组件层是项目当中可以重用的组件,因为依赖数据模型,不能独立出来。最上面一层就是我们的插件层,就是具体的业务功能实现。
其实自动化测试需要解决下面几个棘手的问题:
怎么样找到能够完成这个工作的人
怎么应对频繁的业务变化和UI变化
怎么样设置断言,怎么样建立标准库
我面试非常多的测试人员,发现真正懂自动化测试的人太难找,如果应要找就得去BAT挖人了,这当然是不现实的。很多测试人员是不会编程的,即使会编程,也是太低级的水平。我们的办法是测试人员和开发人员合作完成,开发人员编程能力很强,但他们不知道怎么去做测试,他们互相合作形成优势互补解决了人员问题。这个合作关系当中,测试人员就是产品经理,开发人员通过理解他们的测试用例,来形成需求文档设计。我们的目标是基于我们的APP,开发一个测试框架,这个测试框架具有很高的重用性,可以大大降低测试人员的编程门槛,他们只要简单的调用几行API就完成了测试,测试人员关注的是测试的流程和角度,不需要关心具体底层的实现。下面是我们的工程截图:
我们的测试工程包含了两个包,一个是开发人员维护的,一个测试人员维护的,开发人员去学习Appium框架和API,基于Appium设计和开发出我们自己的框架,同时可以测试IOS和安卓,测试人员不需要去关心这两个平台的区别,因为IOS和安卓的功能都是相同的,所以他们是可以只写一套测试代码的,下面是测试人员的代码:
虽然这是首页的更换城市,我们还有很多其它需要更换城市的地方,其实就是需要换个ID就行了,测试人员是知道怎么通过工具获得ID的,而且这些ID也是相对比较稳定的。
这是一个相对复杂的测试用例,对于测试人员只要知道理解我们的API就行了。
怎么样应对业务和UI的快速变化没有一个统一的解决办法,这是需要针对不同情况找不同的办法,但前提是我们需要对产品,开发和测试有个全局的考虑,从而发现解决的办法。因为我们的软件是基于组件化开发的,实现快速应变有着先天的优势。我们组件化提升我们的开发效率,同时在测试方面我们也应用了针对组件的组件化测试。下面我来举例说明一下,当然这只是我们解决的问题当中的2种类型:
搜索排序是一个非常常见的场景,所有电商APP都有这个场景,我们的也不例外,下面是一些我们的场景图:
上面两个图,一个是进行商家查询,一个是进行服务查询,这两个场景看似不同,但对我们测试框架来讲实现是一样的,没有区别,我们的界面都是组件化的。比如左边的截图包含了题头组件+地址选择组件+赛选组件+列表组件,右边截图也包含了相同的组件,我们的题头组件定义了很多不同展示形式,其实都是一种组件,因为我们的测试框架也是针对组件做的,自然就保证了测试用例的灵活性。上面的赛选排序测试用例就简化成了下面这样,就几行代码,我们的赛选条件可以只多级的,这个示例只是两级,如果我们要设置地区条件排序条件,则filter(“行政区,登州市”),sort(“好评优先”),写测试用例变得非常简单和直接,如下图所示:
其实这个赛选函数可以使用到很多地方,如车型选择,城市选择等等,我们的框架每个函数都是解决的一类问题,而不是一个问题,具有非常好的灵活性。
下面我讲讲怎么建立界面变化的问题,拿商家查询来讲,列表当中的每个CELL,产品那边是经常变化的,赛选条件也是经常变化的,赛选条件变化,我们就是把传入的参数变化一下就行了,对于CELL的变化有稍微复杂些。做过测试的人都知道,测试这个商家列表,我们可以从很多角度进行测试,角度有:排序和赛选条件是否正常工作,正常查询是否能进行,点击CELL是否能成功进入详情等等。
因为CELL是经常变化的,但CELL的变化会影响我们排序和赛选的测试用例吗?如果你使用截图对比方式,当然是影响的,因为你的CELL变化了,说明标准图例也必须变化,否则用例就是失效的。但如果我们使用逻辑进行判断呢?情况就完全不同了,我们的CELL可以任意变化,但商家名称这个字段是永远不变的,没有商家名称,这个列表也就没有意义了,所以可以讲这个字段是100%肯定不会变化的,那么通过截取列表当中商家名称的序列,我们就可以判断排序和赛选条件是否有问题。比如,一组赛选和排序条件设置后,我们希望的商家排序是:
汽车快修保养,盛大汽车装潢,大拇指汽车美容…
但实际上是:
汽车快修保养,大拇指汽车美容,盛大汽车装潢
通过对比两个LIST,我们就可以知道,排序和赛选逻辑是否发生了变化,程序是否出现了BUG,所以我们能做逻辑判断的地方,就不会使用图片对比判断。使用逻辑判断的另外一个好处就是,我们不需要考虑机器的适配性,安卓机器市面上太多了,每种机器的截图都是可能不同的,劳动量显然是我们这样做的几十倍。通过这个例子,我就是想说明一个道理,业务变化快是事实,但不能说自动化测试成本就会非常高,关键是产品+开发+测试的全局的思维和解决问题的方式。
断言的设置和标准库的建立也是一个比较棘手的问题。我们有一个自动化测试标准库,这个库有的是生产数据,有的是后台创建的数据,由后台团队进行维护和升级,这个数据库表结构和生产环境保持同步,就是生产环境的一个特制版本,可以支持生产流程当中的全部业务逻辑。为了保证自动化测试的可重复性,我们使用的DOCKER技术进行映像回滚,APP测试框架可以通过SOCKET连接远程触发数据库回滚和服务器代码回滚。有了标准数据库,下面就是怎么建立对比标的数据。
前些时间我面试了一个做测试的老兵,8年做测试的经验,曾经在阿里和百度都呆过,最后于2014年离开百度到了最近的一家公司。当时我问她的问题是,怎么继续断言的逻辑比对。她给我的答案是,他们测试团队也有开发,他们会编程从数据库读取数据,然后和APP的读取数据进行比对。这虽然是个解决办法,但测试人员需要有良好的开发技能,而且因为业务逻辑变化,他们测试团队读取数据库的方式也需要不断修改。我看过很多人的测试用例代码,包括一些国外的示范例子,很多人喜欢把测试断言判定条件直接写在代码上面,比如ASSERT(result==’example’),这种Hardcode的方式重用性和维护性极差,我们的测试代码是绝对不允许这样做的。我们提出了一个标的数据集的概念,标的数据集就是你需要对比的标准值,这个标准值可以是一段文字,数字,一个字符串LIST(例如商家查询的对比标的),也可以是图片,可以是任何形式的数据,下面的数据就是怎么来建立这些标的数据集。之前我在网上看到一些例子,都是人工去处理的,人工去设置到EXCEL表格当中,人工截图然后放到指定的文件夹里面,每次业务逻辑变化,这个标的数据集的建立本身就是一个费力的事情。我们的解决办法是标的数据集建立的自动化,我们的框架里面有两个测试环境变量,一个是数据标定流程,一个是测试流程,测试流程不用讲了,标定流程其实就是自动化生成标准数据流程。当测试手工完成业务测试后,我们就认为现在的数据可以作为标的数据集了,会启动标的数据集流程,这个流程,系统会截取界面数据,或者自动截图,把这些数据保存在我们规定的目录之下。例如,前面的商家搜索案例,系统会把商家名字排序截取处理,保存到一个字符串LIST的数据集当中,同时也会截图手机图片,作为图像对比的标准图片,并且保存到对应的文件目录中。每个测试用例都需要加入一个数据标定流程,数据标定也提供了API,测试人员只要直接简单调用就行,他不需要关心数据是怎么截取的,怎么保存,或者保存到了什么地方,他都不需要关心,他只需要指定需要针对什么生成什么数据集。例如,商家搜索案例,测试需要指定,按照商家名称的ID,生成一个字符串LIST的数据集,其它的他都不需要去关心。
通过上面的一些措施,我们现在就一个测试人员来维护9个APP,如果有不能完成的测试用例流程,则让开发人员协助帮助他完成。当然基于人员储备考虑,我们后面会增加一个测试人员,保持2个测试人员水平。
没有更多推荐了,含代码 | 支付宝如何优化移动端深度学习引擎?
阿里妹导读:移动端深度学习在增强体验实时性、降低云端计算负载、保护用户隐私等方面具有天然的优势,在图像、语音、安全等领域具有越来越广泛的业务场景。考虑到移动端资源的限制,深度学习引擎的落地面临着性能、机型覆盖、SDK尺寸、内存使用、模型尺寸等多个方面的严峻挑战。
本文介绍如何从模型压缩和引擎实现两个方面的联合优化,应对上述挑战,最终实现技术落地,希望对大家有所启发。
由于移动端资源的限制,大部分深度学习引擎都部署在云端,移动设备获取到输入数据,经过简单的加工,发送给云端,云端服务器经过深度神经网络推断运算,得到结果并反馈给移动端,完成整个过程。
显而易见,这种交互方式有很多弊端,比如依赖网络,流量过大,延迟太长,更重要的是,云端服务器必须有足够大的数据吞吐能力,如果移动端请求量太大,超过负荷,容易导致服务器宕机,从而使所有移动端任务都失效。其实,现有的移动设备已经逐渐从以前的单核32位到多核64位过渡,计算能力和存储能力有了很大的提升,将深度学习引擎部署到移动端已经成为一个必然趋势。
然而,成功将DL引擎部署到移动端并非易事。运行速度,包大小,内存占用,机型覆盖,甚至功耗都是必须逾越的障碍。支付宝移动端深度学习引擎xNN是这方面的佼佼者,本文将回顾xNN移动端DL优化的方法和技术。
2.运行速度
大部分移动端处理器都是基于ARM架构,移动端完成深度神经网络推断的任务,基于CPU的方案是最基础的,也是最可靠的;基于GPU的方案存在兼容性/数据同步/overhead过高/接口不满足等问题;DSP的方案也会存在兼容性的问题; 最近,很多手机芯片厂商开始构建AI协处理器(各种TPU/APU/NPU),但离应用还需要一定的时间。下面我们重点介绍一下在ARM平台的优化技术。做优化有三部曲,如下:
第一部:充分评估的优化目标。如果算法原型太复杂了,花再多精力优化也是徒劳。针对DL业务,务必让模型充分精简,直到你觉得差不多了才开始下手,不然的话,嘿嘿。
第二部:确认运算热点。这离不开一些timing&profile工具,如XCODE instrument,&GPROF,&ATRACE, DS-5等,熟练地运用工具,可以事半功倍。
第三部:贴身肉搏。下面有利器若干。
2.1.基于C/C++的基本优化
编译器很牛逼,GCC/CLANG都有运行速度的优化选项,打开这些选项大部分情况下都会帮你的程序速度提升不少,虽然这还远远不够,但聊胜于无。
书写高效的C代码。循环展开,内联,分支优先,避免除法,查表等等优化小技巧一定要滚瓜烂熟,信手拈来。本文将不再赘述这些基本技巧。
必须学会看得懂汇编,即便你不写,也要知道编译器编译出来的汇编代码的效率如何。这样你可以通过调整C/C++代码,让编译器生成你需要的代码。否则,一切浮于表面。
2.2.缓存友好
基于CPU内存子系统的优化工作很大部分都是在想如何高效地利用缓存(cache),尤其是图像视频处理这种大量数据交换的场景。几十年前,我们的老前辈就发明了主存,多级缓存,&寄存器用来弥补存储器与计算单元的性能差异,直到今天这个问题还没有解决(或许一直都不会解决,存储器和计算单元的设计思路是不一样的,高速ram的成本肯定是高的)。存储层次如下图,原理很简单,你想跑得快,只能背得少,你想跑得快,还想装的多,那就要多掏钱买个车。
CPU-内存 子系统工作起来后,如果寄存器没数了,通过指令从L1&cache拿,L1没数了(Cachemiss),从L2拿,Cache都没有数据了,从主存拿,从主存拿的话, 也要分时间和位置,主存(dram/DDR)不同时刻不同位置访问的效率都是不同的。这里分享几个准则:
尽量复用内存,不要随便地申请一大块内存。访问一大块内存意味着cache&miss、TLB miss、dram切bank的概率都会增大,效率自然就降低。小块的内存反复使用,可以让CPU更加持久地运作,CPU运作占比越高,程序效率越高。要知道,一次cache miss导致的访问主存,在复杂应用下,可能有几十甚至上百个cycle的stall.&
数据访问一般都遵循局部性原理,位置相近的内存被重复使用的概率更高,&cache的替换规则也大多给基于这个原理来设计,跳跃的访问内存会打破这个规则,造成访问效率的低下。
主存和缓存的最小数据交换单元是cache line,&所以访问内存的地址最好是按照cache&line的大小进行,这样可以保证最高效的数据访问。比如某台机器cache&line大小为64Bytes,申请一个128Byte的内存区域,将它的开始地址放在非64Byte对齐的位置,如0x,那么访问这128Byte需要3条cacheline的访问,&若放在0x,&则只需要2条cache line,&一般通过多申请一点点内存,来避免这种不对齐,比如用posix_mem_align()&函数;更近一步,为了对齐访问,有时需要对图像的边界做一些padding的,比如&224x224的图片,我们可能会存储在256x224的内存地址中,保证在随机访问某一行时,地址处于对齐的位置。如:
如果反复的读写一段内存进行运算,效率上肯定不如“一次读取-多次运算-一次写入”来的更高效。比如,DL模型中,一般CONV层后面跟着RELU,BIAS层,本着内存访问能省则省的原则,通过提前分析网络的结构,可以将CONV层和bias合并,甚至将CONV+BIAS+RELU合并在一起进行运算,可以获得很好的gain.&比如:
经过合并后,原来对memory的三读三写,变成了三读一写,速度杠杠的。
显式对齐数据加载
在ARM汇编中,可以显式的通知CPU,加载的地址是一个对齐得较好的地址。比如&& & &&
其中,“:128”&即是通知CPU,&R1存放的地址是一个128bit对齐的地址,此时,CPU在向内存发出数据请求时,可以发出更高效的信号。如果R1不是128bit对齐的,执行这样的指令会得到一个地址异常,也就是LINUX中常见的&bus&error。怎么保证R1是128bit对齐?&程序员必须知道R1对应的数据结构,需要事先设计好地址偏移,保证该指令被正确执行。并不是所有case都可以满足这个要求。
请设想,如果CPU正热火朝天的做计算,这时我们在后台偷偷搬些后面会使用的数据到缓存,下次使用时CPU就不用再去等数据了,效率不是就变高了吗?是的。缓存预取可以做这个事情,如:preload&[R1,&#256],&&可以让CPU在继续执行后面的指令,并开始在后台加载&$R1+256byte位置的数据到缓存中。
但是,preload是一条指令,当你发出这样的指令时,需要知道,至少一个cycle浪费掉了,然后再考虑你预加载进cache的数据,是不是马上就可以接着被CPU 采纳。不幸的是,在手机实时操作系统中,可能多达几十甚至上百个线程嗷嗷待哺,完全无法保证预取的这些数据会被马上用上,系统中有大把事件是会让你的线程找地方歇息的,这种情况下,你预取的数据非但不能用,还可能被其他线程从cache中踢出去,白白浪费了一次主存访问。但是梦想总是要有的,万一实现了呢,总要试试才知道效果吧!
类似的方法可能还能举出一些来,但宗旨只有一个,在做同样的事情的前提下,别让你的CPU经常停下来等数据。
2.3.多线程
手机核备竞赛前几年搞得如火如荼,最近慢慢冷下来了,但也说明多核在运算上还有很大的优势。当然,多核的使用,会导致CPU占比和功耗直线上升,但在可接受的条件下,多线程优化带来的性能提升是最可观的。多线程的实现方法推荐使用OPENMP,接口丰富,编程简洁,用起来并不难,但需要注意一些细节。
OPENMP会自动为循环分配线程,但并非所有循环都适合做多线程优化,如果每次循环只做了非常少的事情,那么使用多线程会得不尝失。线程的创建和切换会消耗一定的系统资源,线程调度有一定的规律,操作系统在没有高优先事件触发的情况下(中断,异常,信号量),&调度周期都在毫秒级别,如果每次线程执行时间没有达到一定的量,多线程的效果就会大打折扣。实际运用中,可以通过&#pragma&omp&parallel&for&if&(cond)&语句来判断runtime过程中是否要启用多线程。
默认情况,OPENMP采用静态调度机制,即将循环的次数平均分配给各个线程,不关心各个线程的执行快慢。如果某次循环运行比较慢或者循环次数不能平均分配时,容易出现负载不均衡的情况,这时就必须有动态调度的机制,动态调度可以根据线程的运行快慢,决定是否“互相帮助”。 OPENMP可以采用schedule(dynamic)来达到动态调度的效果。
同时需要说明的是,某些机型对于OPENMP的支持并不好,或者说线程的开销过大,这时,可能需要手动调整线程的负载和并行的方式。这些细节需要通过反复的实验来微调。
2.4.稀疏化
深度神经网络是个超级黑盒,人们把神经突触的权重找出来了,让整个网络可以完成特定的任务,但却不知道每根突触的作用是啥。实际上,其决定作用的很可能就只是那“几根筋”。我们的实验结果也是如此,大量的权重数据都可以是0,而整个输出精度相差无几。当发现网络中有50%甚至80%的数据为0时,那么针对稀疏的卷积和矩阵优化就显得非常重要了。
稀疏优化的重点是设计合适的索引方案和数据存放方式,如下图。
稀疏方案的应用,依赖不同的运算结构,如针对1x1卷积的优化,数据组织方法和3x3卷积就可能存在不同,不同的稀疏程度,得到的提升效果也是不同,需要不断地尝试各种方案和参数。
2.5.定点化& &&& &
大部分深度神经网络推断引擎,都需要用浮点精度来得到更精确的结果,这样paper上的数据才好看。但实际上,有些DL应用并不需要这么高的精度,即便是单精度浮点,也存在很大的冗余运算。以Tensor的1x1的卷积为例子,实际就是一个乘累加的过程,若使用单精度浮点存放数据,每个元素需要4个字节,而如果将数据量化到8bit, 只需要1个字节,节省3/4的内存访问量,这意味着在带宽紧张的状态下,性能提升会更加明显。下图体现了不同场景下定点化的性能提升收益(倍数)。
2.6.NEON及汇编
NEON 是针对高级媒体和信号处理应用程序以及嵌入式处理器的 64/128 位混合 SIMD 技术。它是作为 ARM内核的一部分实现的,但有自己的执行管道和寄存器组,该寄存器组不同于ARM 核心寄存器组。NEON 支持整数、定点和单精度浮点&SIMD&运算。经过良好设计的NEON代码,理论上可以比普通C语言版本快2-8倍。
NEON指令集分为ARMV7版本和ARMV8版本,寄存器个数和格式略有不同。写NEON指令有两种方式,一种是NEON Intrinsic, 一种是NEON Inline Assembly(内联汇编)。
本文主要是分享一些用Neon/汇编优化的经验,学习具体neon/汇编写法可以参考:
2.6.1.NEON Intrinsic vs Neon 内联汇编
大部分情况下采用NEON&Intrinsic编程就够用了,NEON Intrinsic的好处也是非常明显的,首先,在armv7,armv8平台都可以跑,其次,代码简洁容易理解和维护,另外,编译器还会根据不同平台做代码重排;但是NEON&intrinsic也有一些缺点,比如没有预取指令,分解Neon寄存器很麻烦,寄存器分配可能不高效,无法做显式的对齐加载,编译器可能会引进一些奇怪的指令,造成性能低下。
如果对某个模块的性能要求很高,编译器的输出不满足要求,这时候,就需要使用内联汇编;对于xNN中的核心模块卷积运算,都是通过内联汇编实现,性能比NEON Intrinsic提升10%左右。以下是ARM官方例子:
当然还有一种方式是采用纯汇编写程序,这不是所有人都能接受的。付出得多,收获也多,真正的高效的程序都是纯汇编的。相对编译器产生的代码,手工纯汇编的好处还是非常明显的,比如精简的栈内存,高效的寄存器利用,充分的流水线优化等等,有一种一切尽在掌握的快感。
但是,大部分情况下,用纯汇编写程序花去的精力,完全有机会在其它地方去弥补。所以这种方式适合追求极致的同学。
2.6.2.会写NEON/汇编很重要,构思好的实现方案更重要
NEON指令比较丰富,实现同一个功能有多种指令组合,除了理解指令的本身的作用之后,需要合理组织数据,使用更高效的指令来实现既定功能。
举个例子,实用实现单精度浮点矩阵乘法 sgemm中的 C = A * B + Bias,为了简单起见,假设矩阵的维度是2的幂。如&Dims(C) = 512x512, Dims(B) =512x512, Dims(B) = 512x512, Dims(Bias) = 512x512; &
代码1 &: &C语言的写法是:
运行时间是: 1654689us& (小米5 snapdragon 820,下同). & &
为了构造SIMD,我们将4个C的元素同时输出,如:
从而产生代码2,这是初步的NEON写法:
运行时间:633551us
考虑到数据的重复加载,可以多取四行,一次输出4x4的block, 这样可以省下接近3/4的数据读取,如
从而得到代码3:
运行时间:367165us
进一步,我们发现矩阵B的取数不连续,每次只取4个float, 远不到cache&line (64Byte),相当于cache miss有3/4是有可能被优化掉的,存在很大的浪费,所以对B的数据做转置,转置的过程可以和实际代码结合,减少额外内存拷贝;同时,我们将Bias的作为sum的初始化值, 减少一次写操作。于是得到代码4:
运行时间是:60060us
最后,按4x4的block进行循环构造,然后用OPENMP进行多线程化,得到代码5:
运行时间25178us
上面四种NEON写法的运行速度增益分别是:
代码5肯定还不是优化的终点,通过数据重排,4x4的转置部分还可以省掉;cache的优化还可以更加细致;4x4的块是否是最优也有待论证;最后,还可以祭出汇编大杀器,性能还能再上一个台阶。
sgemm的优化版本还可以参考nnpack/eigen等开源库。实际代码中,需要处理各种维度的情况,代码远比上述要复杂。
移动端的资源紧张,不仅仅是指运算资源,app大小也是商用DL引擎一个重要指标,更小的包大小意味着更快的下载速度,更少的app下载流量。app大小的压缩包括很多方面,这里我们只针对库文件的大小精简,做一些经验分享。
3.1.编译优化
编译器有针对大小的编译选项,比如GCC的-Os, 相当于可以同时打开-O2的优化效果,同时精简生成目标文件的尺寸,生成目标代码后,链接成动态库的时候,可以通过strip命令,去掉多余的调试代码。这些是最基本的优化。针对IOS, 可以通过XCODE下面方法做精简:
BuildSettings-&Optimization Level,XCODE默认设置为“Fastest & & ,Smallest”,保持默认即可
Build Settings-&Linking-&Dead Code Stripping 设置成 YES
Deployment Postprocessing 设置成YES
Strip Linked Product 设置成YES
工程的Enable C++ Exceptions和Enable & & Objective-C Exceptions选项都设置为NO。
symbols hidden by default选项设置为YES。
所有没有使用C++动态特性的lib库(搜索工程没有使用dynamic_cast关键字) Enable C++ Runtime & & Types 选项设置为NO。
上述配置,在IOS设置输出目标为release时,XCODE会帮你自动打开一部分配置。同样的,在Android NDK编译,在Application.mk中配置OPTIMIZE=release,NDK会帮你做绝大部分的优化工作。
针对资源紧张的嵌入式设备,ARM提供了thumb/thumb2精简指令集, 相当于,同样的指令,同时有 16bit 和32bit 两套指令,使用 -MThumb选项可以让编译器优先编译出16bit的指令,在执行时通过内置的译码器,将指令转换成32bit的指令,这样既可以精简代码,还可以保持原来32bit指令的性能。
针对动态库的发布,还可以通过Invisible Symbol的方式,将不需要的符号隐藏起来,省下目标库文件中符号表的表项,如果你的代码有大量的函数,这会是不小的提升,试试看,说不定有惊喜。具体写法是:
这样就ok了,简直是物美价廉!
3.2.代码精简
以上都是一些常规的缩小库大小的方法,实际上,针对DL模型的特性还可以进一步精简库大小,比如包括:
库依赖简化- 大部分开源引擎都会依赖C++ STL库,如Caffe/Tensorflow, 如果要做到极致的精简,需要将复杂的C++属性去掉,这样可以依赖更加紧凑的stl库,甚至不依赖stl.
功能裁剪 - 删除不常用的layer,删除不常用的代码分支,或者Layer组件化,用时加载,都可以减少基本库大小;
3.3.模型压缩
深度学习模型的size,小到几M,大到几百M,如果不做压缩,根本是不可想象的。模型中的大部分数据是神经网络的突触权重,存在有巨大的压缩空间。比如, 支付宝xNN团队提供的xqueeze工具,可以让深度学习模型压缩比例达到几十甚至一百倍。压缩技术包括神经元剪枝 (neuron pruning)、突触剪枝(synapse pruning)、量化 (quantization)、网络结构变换 (network transform)、自适应Huffman编码(adaptive Huffman)等等。具体实现可以参考一些主流的Paper。
4.内存精简
内存使用也需要精简,低端机型有可能只有1G甚至512M的内存空间,再复杂的应用场景下,经常会不够用。精简内存,可以一定程度上缓解内存不足引起的闪退;另一方面,使用更少的内存也有利于推断速度的提升;
具体到DL推断过程运行时存在很大的内存冗余,比如:
实际上,推断过程中,大部分输入层在做完运算后,可以被马上释放,所以完全存在复用内存的可能性。
支付宝xNN 设计了一种称为MPool的内存管理机制,结合深度学习推断的过程,MPool 通过分析网络结构,在内存充分复用的前提下,计算出最小的内存使用量,在开始推断前提前申请足够的内存。
MPool有如下几个优点:
避免推断过程中反复申请释放内存;
内存申请全部集中到初始化过程,避免推断过程中出现内存不足导致的推断失败,从而改善用户体验;
充分的复用可以提高的内存访问效率,对性能提升也有一定的帮助;
采用MPool结构的DL引擎,运行主流模型,内存占用降低达到75%以上。&
5.兼容性与可靠性
商用软件,兼容性和可靠性是很重要的指标。举个例子,支付宝xNN在春节扫五福活动中,机型覆盖率达到98%以上,基本上只要是ARM based的手机,电池不要随便爆炸,就可以支持。然而兼容性和可靠性的提升,不比开发过程顺利多少,需要解决很多工程上的问题。
举几个例子:
为了在支付宝app中部署xNN,需要兼容app中的stl版本;
为了兼容旧版本的Android, 必须使用较旧一点的NDK版本和API, 否则会出现一些数学库的兼容性问题;
为了保证连续多次运行内存不泄漏,需要在不同android/ios版本的上做疲劳测试;
为了多任务并发,需要做互斥;
为了兼容受损的模型文件,需要做多层次的模型校验和兼容。
这样的例子还有很多。总结一句:出来混的,迟早要还的;代码中的任何瑕疵,最后总会被爆出来。
基于CPU的实现方案,面对复杂场景/强实时性应用,还是力不从心的,毕竟CPU资源是有限的,永远都无法预测用户部署的模型的复杂度,也无法预测手机后台运行了多少程序;异构化也是重要的优化方向,采用DSP/GPU来做推断可以大大降低功耗,同时还可以提升推断速度,DSP/GPU方案面临的问题是兼容性比较差,每一个款手机都有可能不同,所以需要做大量的适配工作。
最后,模型安全也是需要考虑的方向,模型文件包含了用户的知识产权,对模型文件适当加密和隔离运行,可以有效地阻止模型被破解和盗取的风险。
喜欢晓雯!记得关注晓雯哟!
如果需要以下书籍,请加QQ群免费分享
没有更多推荐了,}

我要回帖

更多关于 代码组件化 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信