移动性能测试 iOS 性能专项初探

testly · 2015年06月08日 · 最后由 testly 回复于 2017年04月20日 · 5973 次阅读
本帖已被设为精华帖!

前言

iOS 性能测试是什么?

iOS 性能测试是指基于 iOS 平台从:
资源消耗
内存泄露
流量消耗
耗电功率
渲染效果
加载时间
等等.....
这些方面配合一些对应场景去收集数据然后根据数据去分析和定位可能存在的问题!

为什么做这个性能测试?
其实在现在很多企业的功能测试和后台 API 的测试已经相对较为成熟尤其是功能测试。
但是即使是这样,尽管你的应用有多牛 B,你的后台架构有多好,你别忘了你最终还是基于平台来跑,
所以你的应用在平台里面的运行效果不好(Crach 率高达百分之几十,内存泄露遍布整个流程)的话,其他的也都是多余的........

做完有什么用?
这个问题我只能说,只要自己用心去一项项去做!研究透!你会发现很多的问题!
比如你设计几个场景然后把这几个场景一个个单独跑一遍,把数据拿出来分析一下,你会发现资源占用较高或者内存泄露严重的步骤一步一步去检查代码会发现确实有问题存在,有可能是垃圾对象冗余,也可能是接口数据太大,json 解析消耗较高或者其他等等.....

怎么去做?
Instruments 里面集成的很不错了!所以这里用 Instruments 里面的工具来做的!
一项一项来:

1,资源消耗(Cpu、内存):

可以直接用 Xcode 真机 Debug

上图大家都知道怎么看吧,比较简单。也可以用(Activity Monitor)不过没有上面这个方法简单粗暴

2,内存泄露 Leaks:

选中内存泄露红色区域后下面的列表会把泄露的所以相关内容在下面列表展示,你可以直接找到比例最高的方法直接定位到代码里面!
实例:

关于:tableView:didSelectRowAtIndexPath ,分析下它的内存过程:
sushiString 变量通过 autorelease 创建,它的引用计数是 1.

这行代码使得引用计数增加到 2, _lastSushiSelected = [sushiString retain];
这个方法结束时,sushiString 的 autorelease 生效了,这个变量的引用计数减少为 1
当再次执行 tableView:didSelectRowAtIndexPath 这个方法时,_lastSushiSelected 被赋值了新指针,老的_lastSushiSelected 的引用计数还是 1,没有被释放,所以产生了内存泄露。

3,启动耗时

两种方法:一种使用 NSLog,另外一种使用 Time Profiler。

使用 NSLog

    CFAbsoluteTime StartTime;
    int main(int argc, char **argv) {
     StartTime = CFAbsoluteTimeGetCurrent();
     // ...
       }
- (void)applicationDidFinishLaunching:(UIApplication *)app {
     dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"Launched in %f sec", CFAbsoluteTimeGetCurrent() - StartTime);
     });
     // ...
 }

使用 Time Profiler
Instruments->Time Profiler
Profile 你的 app
切换到 CPU strategy view,找到你的 app 启动的第一帧
搜索-[UIApplication _reportAppLaunchFinished]
找到包含-[UIApplication _reportAppLaunchFinished] 的最后一帧,即可计算出启动时间

你也可以粗略看下把有波动的部分选上会有一个时间出来!

3,流量 (Network):

4,加载时间(Time Profiler):
当我们发现 App 有点卡的时候,可以通过 Time Profiler 来看耗时在哪里。

右边的 call Tree 一定要记得选上!下面是这些选项代表的意义!

Separate By Thread:线程分离,只有这样才能在调用路径中能够清晰看到占用 CPU 最大的线程.
Invert Call Tree:从上到下跟踪堆栈信息.这个选项可以快捷的看到方法调用路径最深方法占用 CPU 耗时,比如 FuncA{FunB{FunC}},勾选后堆栈以 C->B->A 把调用层级最深的 C 显示最外面.
Hide Missing Symbols:如果 dSYM 无法找到你的 APP 或者调用系统框架的话,那么表中将看到调用方法名只能看到 16 进制的数值,勾选这个选项则可以隐藏这些符号,便于简化分析数据.
Hide System Libraries:这个就更有用了,勾选后耗时调用路径只会显示 app 耗时的代码,性能分析普遍我们都比较关系自己代码的耗时而不是系统的.基本是必选项.注意有些代码耗时也会纳入系统层级,可以进行勾选前后前后对执行路径进行比对会非常有用.

5,电量消耗(EnergyDiagnostics)

这个主要是看那个 Cpu Activity 吧,我也在待研究状态,我怀疑这个工具并不是很准!

上面这些都是浅浅的跟大家说了一下怎么拿到一些数据!
拿到数据之后把他们收集起来分析一下,你会发现有一些场景或操作的系统资源异常,这个时候你可以抱着怀疑的态度去看看代码,也可以跟开发一起去过一下这场景相关的代码,看看是哪里导致这个消耗较大!数据接口量太大?json 解析导致?对象没有释放?....

欢迎一起交流,一起进步 可以关注我的微信公众号:“测试开发进阶” - 点我关注

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 43 条回复 时间 点赞

我问个问题。Network 这个只有 total 的,你怎么过滤应用,或者针对应用场景怎么办

#1 楼 @monkey NetReport 的 total 针对场景的话可以用步骤结束发送和接收的 total 减掉步骤前的 total 就是步骤所消耗的 total?

#2 楼 @testly 额。。其他应用的流量不会算进去?

#3 楼 @monkey 这是在 Xcode Session->build 调试当前应用的 就算其他应用操作请求网络这里都没有计数的!这里的计数只针对当前应用的流量

#4 楼 @testly 确认?iOS 系统的一些流量会算进去么?比如 apple.xxxx.com 等呢。另外这个只能精确到 0.xkb?

testly #39 · 2015年06月08日 Author

#5 楼 @monkey 反正我的打开任何一个应用都不会计数,唯有被测应用,你说的 apple.xxx.com 会不会占用我真不确定!精确的问题可以有策略的!

#6 楼 @testly 这个有 total 么。。。另外 remote 如果是集群的话,很多 ip 怎么区分?= =

而且 in 和 out 区别开来的。。。然后处以 1024?

#8 楼 @monkey 是这样,不过这个下面的列表是瞬时的,不会一直保存

testly #35 · 2015年06月08日 Author

#7 楼 @monkey ip 是资源请求的地址的 ip 吧 我暂时没有去区分,对我而言都是流量的出和进

#9 楼 @testly 这不是统计起来很麻烦么。。= =。。ip 是资源请求的 ip 地址。但是面对集群的时候,就会有很多服务和 ip,到时候统计起来会很麻烦。。。这个有没有好的方式?

testly #33 · 2015年06月08日 Author

#11 楼 @monkey 麻烦也得做啊! 不要求精确的话可以 直接看上面的 total,无论是集群都是从这边出进的!用 tcpdump 抓包也很麻烦的!

#12 楼 @testly 如果面试别人这样问,你也这样回答别人么。。= =

testly #14 · 2015年06月08日 Author

#13 楼 @monkey tcpdump+wireshark 可以做到,而且精确!你懂得!

#14 楼 @testly 不懂。。。

#14 楼 @testly 不懂。。。

#15 楼 @monkey 你之前不是说发朋友圈弄起来很麻烦么?,没搞定?
使用 rvictl 命令创建 RVI 接口 (remote virtual interface),使用 iPhone 的 UDID 作为参数。
$ rvictl -s
如果想捕获多个设备的网络包,可以使用上述命令创建多个设备的 RVI,传递每个 iOS 设备的 UDID 作为参数即可。
RVI 虚拟接口的命名规则为 rvi0,rvi1,rvi2,…,可使用 ifconfig 命令查看
$ ifconfig rvi0
rvi0: flags=3005 mtu 0
使用完之后需要将创建的虚拟接口移除
$ rvictl -x

使用 tcpdump 抓包
tcpdump -i rvi0 -w capture.cap -v -vv -c 100 -X -e

使用 wireshark 打开 capture.cap 就可以进行分析了

流量本来就可以通过 instrument 去看单进程的。。。。

testly #26 · 2015年06月08日 Author

#18 楼 @vigossjjj 你这个是 network,都可以的

文章排版有很多点乱,让我看了好伤

testly #22 · 2015年06月09日 Author

#21 楼 @lihuazhang 我再改改吧!

#1 楼 @monkey 流量可以用 charles 抓包

#23 楼 @mobilefeng 这个不准的。只有部分的图片,api,而且是 http 的,非加密的那种才拿得到。

#23 楼 @mobilefeng 另外,添加头像

#24 楼 @monkey 新人 谢谢大大提醒~

赞,感谢分享!

能不能不用源码呢

testly #15 · 2015年08月06日 Author

#28 楼 @xiaoxiao 暂时不能把,看以后的趋势应该会有

@lihuazhang @testly 可以的吧,我们已经做出来了,不过性能不怎么好,我们是通过 ssh 在传数据,然后像 android 一样获取内存等信息,再通过 Matlab 来做图

testly #13 · 2015年09月23日 Author

#31 楼 @xiaoxiao 可以发帖分享一下。记得 @ 我哦!谢啦!

mark 收藏

可以自动化么

@testly 求教,现在启动时间那里,启动完后在 sample list 里搜索 UIApplication _reportAppLaunchFinished 结果为空,是什么原因呢?

#35 楼 @jytest 我试了下,确实没找到。

我 google 了下,这篇文章也有提到这个方法,并有截图:http://www.programering.com/a/MTO4ATMwATU.html

文章中的图如下:

然而,我在 iOS 9.3 上对我自己的一个应用进行 profile ,结果却有出入:

调用栈有点不一样。文中调用的是 _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:,而我的是 _runWithMainScene:transitionContext:completion:

换了一台 iOS 7.1.2 的设备,调用栈和文章的一致了:

因此,估计文中的方法不适用于 iOS 9.3 。其它版本的系统目前手上没有,待其它同学测试。。。

至于 iOS 9.3 上应该以哪一帧作为启动结束,我也不清楚。。。 @testly 知道不?

testly #37 · 2016年05月21日 Author

#36 楼 @chenhengjie123 9.3 不是很清楚,我测得时候还是 9.0 以前

#31 楼 @xiaoxiao 你好 请问一下你用代码是怎么实现获取 iOS 性能方面的数据的,如 CPU。

testly #39 · 2016年11月23日 Author

#38 楼 @Mr.Tian 你好,OC 有底层的 API 获取 cpu 和内存 这种基础的性能数据,我这边的话因为要测竞品数据,所以用到了越狱手机,拿数据会方便很多

#39 楼 @testly 我是用 appium+python 做自动化测试的,真机测试不知道怎么去获取这些数据,不知道楼主你有没有类似的资料。

testly #41 · 2016年11月24日 Author

#40 楼 @Mr.Tian 越狱设备可通过 ssh 获取基础性能数据,你自动化时可异步去截取数据

还是忧伤的问一句,图一那里打开的

梦梦GO 回复

xcode debug session

向阳 利用 anyproxy 做 app 网络流量测试 中提及了此贴 03月17日 14:15
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册