声明:本文翻译自《ios performance optimization》,原文作者Khang Vo。翻译本文纯属为了技术交流的目的,并不具有任何的商业性质,也不得利用本文内容进行商业盈利。欢迎转载,但是希望转载的时候加上出处连接,谢谢。译者联系方式setipro@163.com,如果有ios开发之类的问题,欢迎一起讨论,谢谢。另,由于本人翻译经验不多,如果翻译不妥或者理解不到位的地方,希望各位朋友海涵,可以发信到上述邮箱,我会及时地根据大家的反馈,对翻译稿做及时地修改,谢谢!
(逐个上传图片困难,请移步如下连接http://www.cocoachina.com/bbs/read.php?tid=127408,可以看到带有图片的pdf文档)
大家好,我是setipro。
ios的性能优化方面的书籍很少,萌前辈推荐,看了《Pro_IOS_App_Performance_Optimization》这本书,觉得书中内容应该对性能优化方面的工作有写帮助。
由于此书在国内尚且没有翻译版本,所以就生出了自己尝试着翻译此书的念头,并和大家做ios开发方面的技术交流。
翻译本文纯属为了技术交流的目的,并不具有任何的商业性质,也不得利用本文内容进行商业盈利。欢迎转载,但是希望转载的时候加上出处连接,谢谢。译者联系方式setipro@163.com,如果有ios开发之类的问题,欢迎一起讨论,谢谢。另,由于本人翻译经验不多,如果翻译不妥或者理解不到位的地方,希望各位朋友海涵,可以发信到上述邮箱,我会及时地根据大家的反馈,对翻译稿做及时地修改,谢谢
第二章 运用工具对应用进行测试:模拟器和真机测试
在本章,你将学到如下内容:
1.模拟器与真机测试环境之间的区别
2.内存管理如何影响应用的性能
3.测试应用性能的技术和工具,包括如下几点
(1)测试内存和性能的基本工具
(2)从不同的角度来对内存使用进行测试的复杂工具,比如测试内存泄露或者内存非法访问(bad access)
(3)如何将你的程序划分为一些更小的部分,以便更好地对出现的性能瓶颈问题进行定位
为了改进程序的性能,你需要仔细地来进行测试来找出问题所在。如果想要设计出一个有效的测试流程,你必须了解导致一段程序变慢的不同的原因。
在最一开始,你需要在两个方面进行考虑和选择:测试环境与真机环境的选择;内存优化以及性能优化的权衡。
首先,你需要了解模拟器与真机测试环境的不同。
模拟器与真机
一个iphone应用主要的性能问题就是程序运行在一个系统资源匮乏、处理速度缓慢的环境中。在模拟器上运行的iphone程序要比在真实环境的程序快很多;事实上,模拟器上的运行环境和你的开发设备一样快。
所以,当你在真机上运行你开发的应用的时候,你就会有惊无喜地发现,在模拟器上运行地飞快的程序,在真机上却很卡。我已经见过很多开发者抱怨包含网络交互应用性能缓慢的问题。这在很多情况下是真实存在的。但是在很多情况下,可能因为代码本身实现方式的问题而导致程序的性能下降,而不是因为所谓的网络问题。因此,在标准的测试环境下使用基本的测试工具来进行性能调试,会让你对你的程序性能和用户体验更有信心。
为了证明模拟器和真机的不同,我对一个应用分别在模拟器环境和真机环境下进行了测试。结果令人吃惊。
(1)模拟器用了0.5秒的时间完成了一个运算
(2)同样的运算,在真机上花费了7秒的时间。
运行的程序十分简单:我用两个数组进行测试,每个数组包含1000个元素。然后,程序对两个数组进行遍历,然后找出两个数组相同的元素,找到后打印出“hello”。在真正的工程中,你可能并不需要做对两个含有1000个元素进行遍历比较这种运算。但这明显不是问题的重点。我选用这个例子,是为了说明真机的运行环境与模拟器环境的巨大差异。
而这个问题就引出了如下的观点:你必须经常在模拟器和真机上进行测试来保障你的性能。那么,为何不只在真机上进行测试呢?因为模拟器在如下几个方面具有很大的优势:
(1)在模拟器上部署运行程序的速度更快,这对于开发者而言,意味着更快捷的开发,并尽量减少项目延期。
(2)在解决内存泄露以及内存开辟使用方面所带来的问题时,模拟器的表现更好。
存储与性能
存储与性能是不同的。存储通常是指RAM(随机访问内存),而它通常是指你用了多少内存,还有多少内存可用。而性能是指你的应用运行特点。
内存会对程序性能产生巨大的影响。当设备有更多的RAM和存储空间可用的时候,你就能进行预读以及缓存操作。RAM访问要比读写文件以及网络交互快很多。通过进行预读以及缓存操作,你可以在很多情况下大幅度提升程序的性能。举个例子,如果你的应用是个需要加载图片的游戏,更多可用内存是十分重要的,因为你可以在适当的时候对图像进行预读。从内存中读取图片,要比从文件系统中读取快上十倍以上。
但是,更好地运用内存并不意味着更好地性能。有些应用并不需要使用那么多内存;所以,你可以只在性能无法进一步进行提升的时候才进行内存优化。从另一个方面来讲,过度的使用内存来进行优化也不好,因为这有可能导致内存空间不够。
因此,你应当经常在模拟器和真机上进行测试,以便在内存使用和性能优化方面达到一个合理的平衡。
测试工具
测试工具基本划分为以下三种:
(1)XCode自带的基本工具
(2)内存测试工具。可以对内存使用情况以及合法性进行验证
(3)性能工具。这类工具可以对程序的每个部分的运行速度进行测试,而且能够精确地找到性能瓶颈所在。
基本工具
在这部分中,我将使用打log的方法来对程序的各个模块进行性能测试。
记录运行时间
最基本的工具,就是在代码块的开始和结束的地方进行时间记录。通常,记录使用NSLog来实现。通过这个基本工具,开发者可以对程序的每一行或者每一部分来进行测试,测试这些部分用了多少时间来运行。
运行代码块的例子
NSDate *date1= [NSDate date]; for (int i = 0; i < 1000; i++) { // Do calculation here } NSDate *date2 = [NSDate date]; NSLog (@”time: %f”, [date2timeIntervalSinceDate:date1]);
程序返回如下结果
time: 0.0123 (measured inseconds)
工具优点:
(1)简单直接地度量程序性能
(2)你可以对程序的某行或者某段代码进行性能测试
工具缺点:
(1)你不能对UI性能进行测试(比如主UI线程的渲染时间)
(2)你可能会过度优化(在某些代码段上花费过多时间进行优化,而收效甚微)
(3)在模拟器上运行程序通常很快,而在这种运行很快的环境中,NSLog可能无法帮你区分出运行性能的区别。否则,尽管NSLog在设备上运行比较慢,它同样能帮你测试出性能差别。
用途:
(1)无需很多的规划考虑,可以立刻进行测试
(2)当你需要马上得到测试结果的时候
(3)当你需要对某一个小的代码段进行独立的性能测试,以证实你的假设时候
内存工具
对于内存问题,你只要考虑一个方面就好:过度使用内存。在已有的代码中很少会出现内存泄露之类的问题。在新的工程里面,你应该使用全新的自动引用计数技术(简称ARC)。对某些老项目而言,你应当尝试着用XCODE提供的转换工具来对它们进行处理。
然后,并不是每一个工程都能被转换的,在转换的过程中,会遇到很多诸如内存管理原则有关的问题。在试图遵循新的准则去进行内存管理的过程中,也许会导致更多的问题。因此,我会在下面讨论有关对象内存管理的工具,以及内存泄露检测和内存垃圾检测工具。
(注意:所有我在这里介绍的内存工具都可以在模拟器中运行。模拟器的优势在于它能够快速地部署,运行程序。但是,要注意!我强烈推荐同样要在设备上进行测试,因为模拟器和真机并不总是了一样的。他们的编译方式不同,并且有着不同的结构。)
Memory Allocation
Memory Allocation能够帮助你了解你开辟内存的情况。这意味着你可能在内存中开辟并使用大量的内存。由于这些内存还在被使用,所以并没有被释放掉。
Allcation
选择Product->Profile ,然后在打开的窗口中选择Allocation(如图2-1所示)
在选择Allocation工具之后,你将看到一个主要的Allocation选择面板,面板为你提供所有必要的信息,就如你在图2-2中看到的一样。
Allocation面板(图2-2)显示了“已被创造并且仍然在内存中”的情况,以便你查看仍然在内存中的对象,以及哪个对象是最占用内存的。你应该在收到一级内存警告的时候使用这个工具来查看内存的使用情况。
这些细节可以显示具体是哪个类,那行代码来创造,使用,处理这些对象。在这些信息的帮助下,你可以轻松地想出内存的处理方案。这个工具可以对缓存算法和实现对内存的使用情况进行跟踪(第四章有更多的缓存技术细节)。
图2-3和图2-4为你展示了相关细节。细节包括那些对象仍然处于活跃状态,一级哪些对象最为耗费内存。在图2-3中,你可以查看哪些对象在内存中创建,以及在你的应用中的使用情况。
在图2-4中,你可以看到这些对象是由哪些方法创造的。
优点:
(1)这个工具准确地提供了有关应用在运行期何时、在哪种情况下占用了最多的内存。
(2)它也能告诉我们对象在应用中的生存周期。
缺点:
(1)工具的检测结果取决于开发者如何运行应用。它需要在设计测试用例的时候尽可能多地考虑各种情况
(2)设计出一套合理的测试方案来找出最耗费内存的操作,是很费时费力的。
(3)你需要在真机上来进行测试,才能获得诸如内存警告一类的信息。模拟器基本上不会报内存不足的信息。问题就在于,你的模拟器可能和你的电脑一样,有2-4GB的内存可用,但是在真机上的可用内存往往要少得多。
用途:
(1)如果你在测试的时候收到了内存警告的信息,本工具应该是第一个你应该用来测试调试的。
Legacy Code
对于以往的代码来讲,能够将代码从手动管理内存模式转化为ARC模式的工具有可能会转换失败。为了能让你的代码转为ARC模式,这个工具可能会让你去修改你的现有代码的很多地方。也许某个你正在使用的开源库并不方便直接转为ARC模式,而你也不想这么做。因此,我觉得多了解一些手动管理内存的背景知识是有好处的。
内存泄露
当你在内存里面创建了一个对象而没有在适当的时候对其进行释放,内存泄露就发生了。那个对象将一直在内存中,直到整个应用退出。结果就是你的应用没有足够的内存去让它运行的更快,情况有可能更糟,IOS甚至会在某些时候强迫你的应用退出。
Static Analyzer
这是一个简单而直接的工具,用来检测内存泄露。如图2-5和2-6所示,这个工具会告诉你某行或者某段程序会有可能造成内存泄露。
如图2-5所示,你需要选择Product->Analyze或者Command+Shift+B。
正如你在图2-6看到的,第十九行的str对象一直都没有被释放;在这种情况下,Static Anylyzer提供了正确的警告。
优点:
(1)这个工具能让我们对哪里可能出现内存泄露有个大概的,快速的了解
(2)测试过程可以快捷地进行:工具只是对代码进行了编译。Static Analyzer并不运行代码。
(3)这个工具并不需要程序员做什么工作,你只需要点击Build以及Analyze就可以了
缺点:
(1)有时候它并不准确。有时候它会给出错误的内存泄露警告。
用途:
(1)开发者应该优先使用这种工具进行内存泄露检测,因为这个检测过程方便快捷
Leaks工具
在程序运行的时候,把这个作为内存泄露的检测工具是更好的选择。它能确定内存是真真正正地泄露了;当某个对象造成内存泄露,它会通知用户。你尝试着对应用的不同方面进行测试时,Leaks工具可以报告内存泄露具体发生的地方。
在Leaks这个工具的水平方向,你可以发现垂直方向的的柱状图。你可以根据红色柱状图的高度来判断你的应用在那一时刻有多少内存泄露了。
而后,当你想深入地了解内存泄露的具体情况的时候,你可以看到一个记录内存泄露详细情况的清单。通过查看应用名称以及对相应责任库进行排序的方式(在下图,就是LeaksViewController),你可以找到两个内存泄露的对象。快速地扫一眼,你会发现泄露的对象是位于RootViewController的两个图片。
如图2-8所示,在显示内存地址的旁边,有一个灰色的箭头。点击那个灰色箭头,Leaks会为你在代码中明确指出导致内存泄露的地方(见图2-9)。
现在,你可以看到到底是哪行导致的内存泄漏了。通常,Leaks会告诉你导致内存泄露的详细信息,所以你可以很容易地修复此类问题。
工具优点:
(1)Leaks非常的精确,而且提供很详细的信息
工具缺点:
(1)测试结果取决于开发者如何运行应用。用这个工具测试此类问题需要尽可能完备的测试方案,方案最好能涵盖所有的情况。
(2)因为开发者需要运行多次来查看应用运行状况,测试的过程会变得比较缓慢。
工具用途:
(1)这个工具应该在Static Analyzer使用时候被使用。他可以用来找到和解决StaticAnalyzer没有找到的错误。
我推荐你应该先运行Static Analyzer这个工具。而后,如果你仍然比较担心内存的使用问题,或者你在运行的时候收到了ios给的内存警告,你应该使用Leaks工具。
内存垃圾
在本书中,内存垃圾和性能问题并没有太大的关联。然而,如果你的程序崩溃了,那么这就比性能差还要糟糕的多了。因为程序崩溃会把你想给用户提供的良好体验彻底毁灭掉。因此,你应当了解如何更好地运用内存。
Zombie工具
你应该在工具栏选择Product->Profile->Allocations
然后你就会高涛一个运行时的检测工具。问题是,这个工具并不能帮你解决Zombie的问题,所以你需要停止它的运行。然后你需要设置Allocations来配合检测Zombie工作。换句话说,当应用崩溃的时候,这个工具会报告到底是哪里发生了崩溃。
现在,你可以你没必要在意图2-10的下半部分的数据了;你只需要注意如何配置Allocations工具来检查因为Zombie导致的崩溃。图2-11会为你展示如何配置。
在对Allocation进行配置后,你需要再次运行record操作;可以在模拟器或者真机上进行检测。
然后你只需要继续对应用进行不同方案的测试用例,直到程序崩溃。Allocation工具会为你提供相应的信息,如图2-10所示。
如果你点击箭头来查看相应的细节,工具会为你提供一份某个对象(变成Zombie的对象)的内存行为表,表中记录了诸如malloc, autorelease,retain, release(如图2-13所示)。你的实例工程名叫做ZombieDebug,你只需要看表格的第二,三,四行。
当你选择了图2-13的相应行,工具会找到对应的内存操作代码块,如图2-14所示。
工具优点:
(1)本工具提供了导致内存崩溃的具体信息
(2)本工具可以在模拟器上进行
工具缺点:
(1)测试结果取决于开发者如何运行被测试应用。Bad Access错误有时候只在特定的情况下发生。
用途:
(1)本工具只有当你接收到EXEC_BAD_ACCESS的错误的时候才应当使用。
性能检测工具
内存问题,并不是性能问题唯一要考虑的方面。还有一些其他的技术来改进应用性能:比如更好地处理从文件或者网络读取数据,改进用户界面的响应速度,使用多线程。如果想要检测应用的运行性能的话,你可以对下面指标进行检测,包括CPU使用情况,运行时间,还有电池消耗情况。还有,这些指标有可能是相互关联的,稍后我将解释他们之间的关系。
CPU方面的问题往往体现在它在最坏的情况下同时处理多少个任务。最坏的情况就发生在CPU有太多的任务要处理的时候。通常,当没有任务的时候,CPU不应该运行,只有有用户交互的时候,CPU才会以事件驱动的方式来启动,运行。但是,当有很多的任务同时被处理的时候(比如用户输入,读写文件,网络交互),CPU不应该有空闲的时间。当有很多任务要处理,而CPU此时又处于空闲状态的时候,那就意味着你的CPU没有被有效地使用。当你想在任务繁忙的时候充分利用的CPU,那么你就需要使用多线程技术了,之后我会重点介绍几个工具来帮助你跟踪线程的使用情况。
当你需要获取数据的时候,通过文件读写或网络交互是很耗费时间的。因此,你需要很好地在网络和文件之间权衡你的数据读取策略。如果你频繁地从文件或者网络读取数据的话,那么你的用户就会在数据被读出之前等待很久。但是,如果你过早或者过多地将数据读到内存中,那么你在运行其他的任务的时候就会因为内存不足而收到内存警告。因此,测试文件读写和网络行为对于开发者而言是十分重要的。
应用运行的良好程度与用户对性能方面的体验之间有着天壤之别。你的应用也许对内存有着良好的管理,并且有非常快的算法来处理各种数据,但是用户有可能仍然觉得很慢,或者没法对他们的操作及时的做出响应。原因就在于UI线程太忙了,或者它接收到了太多的用户操作。在这类情况下,你可以对你的UI线程工作情况进行检测。
对于IOS系统来讲,有一点尤为特殊。通常,智能手机系统是基于电池运行的。能源消耗对这些系统尤为重要,因为用户不可能一天到晚都为设备充电。同样的,手机的电池电量也不可能像笔记本电脑电池那么大。台式机和服务器在这个问题上有很大的不同,他们都是一直接着电源的,也就是说,相比手机这类手持设备,他们有着近似无限的能源可以使用。因此,你需要仔细地对你的程序进行电源能耗方面的测试。
应用测试工具
你可以选择工具栏的Run->Profile打开,或者你可以通过Command+space的按键,然后输入"Instrument"来打开。Mac OS会给你一些选项,如图2-15所示。
工具分为两类:ios模拟器可用工具;ios设备所用工具。有些工具在两种情境下都能使用,但是需要注意的是,同一种工具可能在两种使用情况下有着不同的表现。就像我已经介绍的那样,ios模拟器与ios设备真的有很多的不同点。
CPU 测试用具
CPU测试工具是最重要的测试工具之一。当你程序运行的时候,这个工具能告诉你CPU的不同行为。当你运行你的程序的某个特殊任务的时候,它可以告诉你CPU处理这个任务的繁忙程度,还有运行时间。
CPU Sampler
这个工具可以对应用以固定的频率进行取样;默认的频率是10ms,但是你可以更改这个值。当CPU每次被取样的时候,这个工具就对其进行一次记录。这些记录和样本通常足够你来辨识性能瓶颈所在,并且对其进行修复了。
如图2-16所示,CPU sampler为你提供有关系统负载和负载用户有关的数据。系统负载就是诸如文件读写,网络和逻辑处理方面。负载用户就是指诸如UI,用户交互之类的。
图2-16展示了这个工具的大致用法,并且从图中可以看到,系统和用户负载除了在某些点上有些起伏,大致的情况是稳定的,但是到了样本的最后部分,这些指标都会有一些比较大的变化。这些信息通常还不足以让我们去确定问题所在。因此你需要更详细的信息。
在我们继续操作之前,你需要勾选名为Hide System Libraries的选项来设置Sampler,以便我们能查看相关的代码。如图2-17所示。
然后,在相应的信息列表中,我们可以找到一个主要方法调用的列表,如图2-18所示。列表中还包括这些方法在样本时间中调用的次数,以及在处理时间段内所占用的时间百分比。
你也可以把运行次数栏改为显示运行时间。后者可以显示各个方法需要多长时间来运行,如图2-19所示。
点击每个方法名左边的小箭头,你可以看到每个方法的调用栈的具体信息,如图2-20所示。
工具优点:
(1)这个工具非常准确。它可以告诉我们应用何时,在哪种操作情况下最为耗费处理器时间,并为此提供详细的信息。
工具缺点:
(1)测试结果依赖于开发者设计的测试用例。测试结果可能取决于任务优先级,以及方法的调用顺序。
(2)设计一套能够涵盖大多数情况的测试方案是很费时间的。
(3)如果想要了解每个方法在iOS环境中真正的运行时间,你需要在真机上进行测试
工具用途:
(1)如果开发者已经了解引发性能瓶颈问题的代码部分,并且希望了解更多的细节,比如哪个方法或者哪种调用方式会导致性能问题的话,那么推荐使用这个工具。
Activity Monitor
这个工具可以在应用运行在设备的时候,显示CPU时间、物理内存、虚拟内存以及线程的数量。当应用运行在操作系统的时候,这个工具可以提供整个应用运行的概况。因此,你可以了解应用在何时需要最多多的CPU时间,物理内存以及虚拟内存。iphone4只有500MB的内存可用,所以你使用的内存不能超过这个数字。如果把这个工具的测试结果和CPUSampler以及Allocation工具来结合,你就能对整个应用的运行情况有个大概的,整体的了解。
工具优点:
(1)在应用运行在你的系统的时候,本工具能够展示一个总体的性能表现数据。
工具缺点:
(1)不提供运行在应用中的各个方法的细节情况。
(2)开发者无法确定性能瓶颈的具体位置,但是他们可以通过内存或者CPU的百分比来进行猜测
工具用途:
(1)本工具应该配合其他能查看细节的工具使用,比如CPU Sampler以及Allocation 工具。
时间测试
现在,让我们来讨论有关性能的另外一个重要话题:时间。通常,当用户抱怨你的应用的时候,他们只会告诉你应用在这里或者那里运行的很慢。因此,你需要测试你的某个任务的完成时间长短。
Time Profiler
和CPU Sampler类似的是,Time Profiler会让开发者在应用运行时间这个方面有一个大概的了解,尤其是哪个方法在你的应用中运行的时间最长(见图2-22)。Time Profiler和CPUSampler唯一的区别是,前者告诉我们方法的运行时间,后者是一个方法的CPU占用情况。
如图2-23所示, Time Profiler就像CPU Sampler一样,显示每个被调用的方法,并且你可以对每个方法进行追踪,了解该方法在自己调用栈上所运行的时间。
工具优点:
(1)Time Profiler在每个被执行任务的运行时间上,为大家提供详细信息。
工具缺点:
(1)运行结果取决于开发者如何运行应用。运行结果也许有赖于任务优先级以及方法的执行顺序。
(2)设计能够涵盖大多数情况的测试用例是耗费时间的。
(3)如果你想要了解每个方法在真正的IOS系统的运行时间,你需要在真机上进行测试
工具用途:
(1) 如果开发者已经了解了性能瓶颈大概是由哪些代码引发的,并且需要测试以证实自己的猜测,或者希望了解具体是哪些代码导致的这些问题,那么推荐使用本测试工具。
(第二章未完待续)
本文详细介绍了iOS应用性能优化的全过程,包括模拟器与真机测试环境的区别、内存管理和性能优化的权衡。通过具体案例分析了性能测试工具的使用,如XCode自带工具、内存测试工具(如MemoryAllocation、Leaks)、性能测试工具(如CPUSampler、TimeProfiler)等。强调了在不同测试环境下的性能测试方法和注意事项,旨在帮助开发者提高应用性能和用户体验。
456

被折叠的 条评论
为什么被折叠?



