【背景介绍】 javascript
虽然iPhone的性能愈来愈好,但app的功能也愈来愈复杂,性能历来都是移动开发的核心关注点之一。咱们说一个app性能好,不是简单指感受运行速度快,而应该是指应用启动快速、UI反馈响应及时、列表滚动操做流畅、内存使用合理,固然更不能出现简单的crash了。 java
那么iOS的性能测试是什么:资源消耗、内存泄漏、流量消耗、耗电功率、渲染效果、加载时间。。。 python
如下将结合iPhone浏览器从启动时间、加载时间、内存占用、CPU和流畅度等维度介绍如何完成一个iOS app的性能测试。其中会用到Apple的性能分析神器”Instruments”。 web
1、启动时间 json
移动应用的启动时间是用户体验的一个重要方面,苹果一直建议尽量的缩短启动时间,防止用户不肯意使用它们。对于浏览器而言,因为程序启动时还会有教育页和闪屏的下发,所以启动时间的获取显得尤其重要。 浏览器
启动时间分为冷启动时间和热启动时间,所谓的“冷启动”,就是一个彻底没有运行的应用的启动时间,与热启动(应用已经在后台运行,某个事件将其带至前台)相比,因为此时系统还没有创建缓存,所以冷启动每每要较平时(热启动)耗费更长的时间。 缓存
要获取准确的app启动所需时间,最简单的就是经过性能打点的方法。首先在main.c中添加以下代码: app
CFTimeInterval startTimeLog; async
int main(int argc, char *argv[]) { ide
startTimeLog = CACurrentMediaTime();
而后在AppDelegate的回调方法application:didFinishLaunchingWithOptions中添加:
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat launchTime = CACurrentMediaTime() - startTimeLog;
NSLog(@"launch: %f", launchTime);
});
可能你会疑问为何这样能够得到系统启动的时间,由于这个dispatch_async中提交的工做会在app主线程启动后的下一个runloop中运行,此时app已经完成了载入而且将要显示第一帧画面,也就是系统会运行到-[UIApplication _reportAppLaunchFinished]以前。
下图是用Instruments工具Time Profiler跑的调用栈信息。
图1
因此使用Time Profiler一样能够查看app的启动时间,具体方法以下:
1. Instruments->Time Profiler
2. Profiler你的app
3. 切换到CPU strategy view,找到你的app启动的第一帧
4. 搜索-[UIApplication _reportAppLaunchFinished]的最后一帧,便可算出启动时间(图中为_reportMainScreenUpdateFinished:)
为了拿到真实的用户数据,追踪版本之间的数据变化,目前浏览器线上版本中启动时间已做为性能埋点上传,这样咱们就能够计算出每日不一样机型不一样OS的平均启动时间,以帮助更加实时有效的监控线上的性能质量数据。
2、网页加载时间
据Google Analytics数据,目前移动网页平均加载时间至少须要7秒,Google的目标是把这个时间降至1秒,由于参考Nielsan Norman Group 的调查研究结果:若是移动网页加载时间超过 1 秒,用户就不肯停留在页面上了。
在目前的技术基础上,在几百毫秒内加载数个网页几乎是不可能实现的,但在1秒内完成移动网页首页内容加载是有可能的,而剩余内容则慢慢加载。所以网页加载首屏展示时间成为了衡量手机浏览器的一个重要性能指标,计算方法为从开始加载网页到首屏内容所有展示所用的时间。
网页加载时间一样能够基于打点的方法得到。移动网页的加载都是从Webview的url请求开始的,webview的操做都会有UIWebviewDelegate的方法代理完成,所以经过对webview代理方法的研究(见下图),选取正确的方法做为开始时间和结束时间,便可获取网页的首屏加载展示时间和网页加载完成时间。
图2
对于竞品,咱们则能够在越狱机型上经过动态库hook的方法进行加载时间的计算,简单介绍以下:
1. class-dump-z命令得到应用程序的类信息
a) 导出设备上预装应用的类信息(/Applications)
b) 导出从AppStore下载的app的类信息,须要clutch命令破解(/var/mobile/Applications)
2. 用GDB或Cycript进行运行时分析(Cycript是一个理解Objective-C语法的javascript解释器),hook当前运行的进程,打印当前运行的viewcontroller及对应的方法名;
3. 动态库注入,执行method swizzling,在对应的方法中打印时间日志
关于如何使用运行时分析及动态库注入,这里就不详细说明了。
固然,加载时间的查看也能够借助于Apple的性能分析神器Instruments。当咱们发现App有点卡的时候,就能够经过Time Profiler来查看耗时在哪里,如图突出的范围就是步骤消耗的时间。
图3
在这里同时分享一个基于摄像+分析的快速进行启动时间和加载时间计算的方法。当前手机的摄像头基本上都支持高FPS的拍摄,拍下来的MP4文件能够经过免费的Avidemux工具来看具体的帧信息,也能够看到帧的时间戳,根据拍摄的格式,目前我测试的视频能够达到30毫秒级别,彻底知足性能测试的需求。
3、内存测试
iOS系统中内存限制是较为严格的,所以内存优化也就成了iOS app一直以来的难题。关于内存测试的方法有不少,能够直接用Xcode真机Debug查看,也能够经过Instruments中内存相关的模板Activity Monitor得到。
图4 Xcode调试查看内存
图5 Activity Monitor查看内存
在实际性能测试中,内存测试每每会分场景进行,经过脚本模拟用户经常使用场景操做,分析该场景下的内存占用状况。
1. 指定run.js脚本测试
$ instruments -w ${UDID} -t ${template} ${APP} -e UIASCRIPT ${script} > .input.log
2. 解析ActivityMonitor模板的trace文件,生成对应的json格式数据
$ instruments_parser -p process_name -i result.trace
其中一个json块数据格式参照以下:
{
"Threads" : 12,
"UnixSyscalls" : 14314,
"Command" : "com.baidu.ime.Ba",
"VirtualSize" : 718213120,
"ContextSwitches" : 5774,
"Ports" : 166,
"PageIns" : 4881,
"Shared" : 12976128,
"PPID" : 1,
"CPUUsage" : 0,
"UID" : 501,
"TotalMicroSeconds" : 307788,
"Timestamp" : 1421818303.125269,
"VPrivate" : 29028352,
"Date" : "2015-01-21 13:31:43:125",
"MessagesSent" : 4042,
"PID" : 721,
"TotalSeconds" : 1,
"Private" : 9338880,
"PGID" : 721,
"MachSyscalls" : 7186,
"ResidentSize" : 39362560,
"Architecture" : 16777228,
"Faults" : 19274,
"MessagesReceived" : 1709
}
3. 统计ResidentSize、VirtualSize字段,使用python的matplotlib图形库生成内存变化图表。
然而对于内存测试,若是你以为只是须要跟踪app的内存使用状况,那么你就错了。一套完整的内存管理测试方案须要关注的点其实还有不少,好比使用Leaks分析内存泄漏,使用Allocations分析内存浪费,使用Zombie分析野指针,使用VMTracker测试虚拟内存,代码中是否仍使用ARC机制等等。
其中关于虚拟内存的测试或许是最容易被忽略的,浏览器就曾经发现过实际内存占用不高,但虚拟内存上涨很快,从而致使app由于内存不足被系统kill的问题。
那么如何分析app的虚拟内存呢?咱们能够经过Instruments的VM Tracker进行查看。VM Tracker主要用于记录app的虚拟内存分配,该模板会显示app中分配了多大的虚拟内存空间,其中多少是Dirty的内存,有多少是被映射到实际物理内存中,而且能够显示详细的虚拟内存分配状况。
图6 VM Tracker查看虚拟内存
关于上图中的dirty size,这里介绍一下dirty & clean的概念。
在程序使用的内存page中,iOS区分两种内存,一种为clean,一种为dirty。
clean page的概念为全部能够被废弃而且从新生成的page,例如二进制代码等从磁盘读取的文件,例如不曾读写过的page,或者被标识为可擦除的内存等。
dirty page的概念为没法从新生成的page,即app生成的,而且已经写入过的page,例如使用malloc分配的heap内存,全局变量,stack内存等。
当系统发现可用内存较少时,会将resident中的clean page进行清除,当有须要使用时直接从磁盘读取就行。系统不能卸载掉dirty memory,由于iOS是没有内存置换机制的。当dirty memory达到一个上限时,应用会被kill,由系统回收内存。
说到上限,这里可能有人会问,在iOS设备中打开不少app后,打开被测app,该app占用内存的上限能达到多少呢?咱们能够经过demo app,手动malloc内存,也能够经过instruments查看,观察内存警告时,App被kill时的日志输出。
下表列出了对各类设备进行测试后获得的数值,供你们参考。
图7 不一样设备内存占用限制
4、CPU测试
CPU测试的方法和内存较为相似,能够经过Instruments中的Activity Monitor模板查看,也能够经过客户端打点的方法获取。
在浏览器性能测试中,重点模块的CPU测试还须要针对不一样机型不一样Architecture指令集进行兼容。例如在iPhone浏览器播放内核库的测试中就须要兼容armv七、armv7s、arm6四、i38六、x86-64五种CPU上都通过测试。
5、流畅度
对于浏览器而言,会存在着较多网页浏览、动画显示等操做,这时是否存在卡顿对于用户体验就显得较为重要。关于流畅度的测试咱们能够经过使用instruments的core animation工具,浏览网页或加载动画,查看fps的帧数。通常而言,当用户操做时,若是fps帧数小于40,则说明存在卡顿的情形。
图8 Core Animation查看fps帧数
百度将于近期推出技术类图书《如何高效地开发一款高质量的移动APP》(名称待定),这将是百度首次技术输出,选题聚焦在移动互联网领域,内容覆盖APP开发、部署、测试、分发、变现、监控和数据分析的全过程。帮助移动APP开发者,更好的了解百度的领先技术、项目经验以及自主研发工具等。
做为业界领先的移动应用一站式测试服务平台,百度MTC覆盖移动应用从开发、测试到上线、运营的整个生命周期,为广大开发者在移动应用开发测试过程当中面临的成本、技术和效率问题提供解决方案。本次出书稿件将陆续在MTC学院发表(http://mtc.baidu.com/academy/article),同步覆盖其余技术论坛,并将在今年上半年集结成册,正式出版发行,敬请期待吧!