性能计数器通常被用来衡量被测系统当前的状况和进行性能测试结果分析。单一的新能计数器通常反映了系统性能的侧面,在进行性能测试结果分析时,一般要多个性能计数器进行分析。本章按照操作系统对性能测试过程中使用到的计数器进行说明。
操作系统计数器可用来监控操作系统级别上的系统性能表现。
Windows操作系统的主要计数器如表2.1所示:
表2-1 Windows操作系统的主要计数器
内存分析用于判断系统有无遇到内存瓶颈,是否通过增加内存来等手段来提高系统性能表现。
内存分析需要使用表2-1中的Memory和PhysicalDisk类别的计数器。以下是内存分析的主要方法和步骤。
该指标是描述系统可用内存的直接指标。如果该指标的数据比较小,系统可能出现了内存方面的问题。
windows和linux操作系统提供这三个指标来支持磁盘交换次数的统计,这三个指标直接反映了操作系统进行交换的频度。如果Pages/sec、Pages Faults/sec数值很大而且Pages Read/sec计数值超过5,则可以判断为内存方面的问题
如果Pages Read/sec值很低,但%DiskTime和Average Disk Queue Length的值很高,则可能是磁盘瓶颈;如果队列长度增加,但Pages Read/sec并未降低,则是由于内存不足。
处理器(CPU)也可能是系统的瓶颈,如下是针对处理器进行分析的步骤:
磁盘I|O也是影响系统性能的一个关键因素。如果所分析的计数器指标来自于数据库服务器、文件服务器、流媒体服务器,磁盘I|O更容易成为瓶颈。磁盘I|O的分析方法如下:
一般来说,定义Transfer数值小于15毫秒为优秀,介于15~30毫秒之间为良好,在30~60毫秒之间为可以接受,超过60毫秒则需要考虑更换硬盘或硬盘的RAID方式了
UNIX/LINUX操作系统的主要计数器表2-2所示。
表2-2 UNIX/LINUX操作系统的主要计数器
PS:LINUX操作系统的命令与UNIX稍有差别。UNIX系统的主要计数器监控命令是vmstat、iostat、top、sar、和seg(图形方式,需要XServer支持);而在Linux中,没有iostat命令,iostat可以监控的计数器包含在vmstat命令中,可以通过参数控制。
进程管理的重点是进程的执行。在内核中,这些进程称为线程,代表了单独的处理器虚拟化(线程代码、数据、堆栈和 CPU 寄存器)。在用户空间,通常使用进程 这个术语,不过 Linux 实现并没有区分这两个概念(进程和线程)。内核通过 SCI 提供了一个应用程序编程接口(API)来创建一个新进程(fork、exec 或 Portable Operating System Interface[POSIX] 函数),停止进程(kill、exit),并在它们之间进行通信和同步(signal 或者 POSIX 机制)。
内核所管理的另外一个重要资源是内存。为了提高效率,如果由硬件管理虚拟内存,内存是按照所谓的内存页 方式进行管理的(对于大部分体系结构来说都是 4KB)。Linux 包括了管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。
不过内存管理要管理的可不止 4KB 缓冲区。Linux 提供了对 4KB 缓冲区的抽象,例如 slab 分配器。这种内存管理模式使用 4KB 缓冲区为基数,然后从中分配结构,并跟踪内存页使用情况,比如哪些内存页是满的,哪些页面没有完全使用,哪些页面为空。这样就允许该模式根据系统需要来动态调整内存使用。
为了支持多个用户使用内存,有时会出现可用内存被消耗光的情况。由于这个原因,页面可以移出内存并放入磁盘中。这个过程称为交换,因为页面会被从内存交换到硬盘上。内存管理的源代码可以在 ./linux/mm 中找到。
虚拟文件系统(VFS)是 Linux 内核中非常有用的一个方面,因为它为文件系统提供了一个通用的接口抽象。VFS 在 SCI 和内核所支持的文件系统之间提供了一个交换层,如下图
在 VFS 上面,是对诸如 open、close、read 和 write 之类的函数的一个通用 API 抽象。在 VFS 下面是文件系统抽象,它定义了上层函数的实现方式。它们是给定文件系统(超过 50 个)的插件。文件系统的源代码可以在 ./linux/fs 中找到。
文件系统层之下是缓冲区缓存,它为文件系统层提供了一个通用函数集(与具体文件系统无关)。这个缓存层通过将数据保留一段时间(或者随即预先读取数据以便在需要是就可用)优化了对物理设备的访问。缓冲区缓存之下是设备驱动程序,它实现了特定物理设备的接口。
网络堆栈在设计上遵循模拟协议本身的分层体系结构。回想一下,Internet Protocol (IP) 是传输协议(通常称为传输控制协议或 TCP)下面的核心网络层协议。TCP 上面是 socket 层,它是通过 SCI 进行调用的。
socket 层是网络子系统的标准 API,它为各种网络协议提供了一个用户接口。从原始帧访问到 IP 协议数据单元(PDU),再到 TCP 和 User Datagram Protocol (UDP),socket 层提供了一种标准化的方法来管理连接,并在各个终点之间移动数据。内核中网络源代码可以在 ./linux/net 中找到。
Linux 内核中有大量代码都在设备驱动程序中,它们能够运转特定的硬件设备。Linux 源码树提供了一个驱动程序子目录,这个目录又进一步划分为各种支持设备,例如 Bluetooth、I2C、serial 等。设备驱动程序的代码可以在 ./linux/drivers 中找到。
尽管 Linux 很大程度上独立于所运行的体系结构,但是有些元素则必须考虑体系结构才能正常操作并实现更高效率。./linux/arch 子目录定义了内核源代码中依赖于体系结构的部分,其中包含了各种特定于体系结构的子目录(共同组成了 BSP)。对于一个典型的桌面系统来说,使用的是 i386 目录。每个体系结构子目录都包含了很多其他子目录,每个子目录都关注内核中的一个特定方面,例如引导、内核、内存管理等。这些依赖体系结构的代码可以在 ./linux/arch 中找到。
优点:低频高能且耗电量较少,高端智能机必备CPU
缺点:价格不菲,对应的手机价格也很高,OMAP3系列GPU性能不高,但OMAP4系列有了明显改善,数据处理能力较弱。
优点:CPU主频高,速度快
缺点:耗电、每频率性能较低
优点:主频高,数据处理性能表现出色,拥有最广泛的产品路线图,支持包括智能手机、平板电脑、智能电视等各类终端,可以支持所有主流移动操作系统,支持3G/4G网络制式
缺点:图形处理能力较弱,功能耗较大。
优点:耗电量低、三星蜂鸟S5PC110单核最强,DSP搭配较好,GPU性能较强
缺点:三星猎户双核发热问题大,搭载MALI400GPU构图单一,兼容性不强
优点:很好继承和发挥了PXA的性能
缺点:功耗大
优点:最早上市的双核CPU,搭载的GeforceULP面积小,性能强,功耗较低
缺点:Tegra2因为功耗问题去掉了NEON,导致视频解码问题大,支持硬解格式少
优点:是2012年业界体积最小的四核A9架构处理器。他是一款高性能CPU,是华为自主设计
缺点:性能不了解
Android 在使用网络的最佳实践是使用3级缓存的设计来提升系统的流畅度并节省流量:CPU 首先尝试从内存中加载图片,若此时图片存在在内存中则加载成功,否则内存会从磁盘中加载图片,若此时图片存在在磁盘中则加载成功,否则磁盘会最终向网络中下载图片。
其实上述的执行逻辑,也就解释了磁盘是如何影响系统流畅度的:对于系统流畅度(其实也是各个应用的流畅度)影响最直接的就是 CPU 的执行效率,但是如果这个过程中内存、磁盘以及网络的读写速度如果跟不上 CPU 的执行效率的话,就会造成 CPU 在处理任务的时候需要花费时间等待数据,从而影响了流畅度。
所以第一个问题就弄清楚了:磁盘的读写速度的降低会使得系统流畅度变差!那么,我们要分析的问题就转化成:磁盘在长期使用的过程中,其读写速度会不会降低。
通过资料查阅,目前,Android 手机大多采用 NAND Flash 架构的闪存卡来存储内容。NAND Flash 的内部存储单位从小到大依次为:Page、Block、Plane、Die,而一个 Device 上可以封装若干个 Die。下图就是一个 NAND Flash 组成结构的示意图。
为了方便理解,针对一个 Die,我们再抽象一下,Page、Block、Plane、Die 的关系如下图所示。
虽然NAND Flash 的优点多多,但是为了延长驱动器的寿命,它的读写操作均是以 Page 为单位进行的,但擦除操作却是按Block 为单位进行的。
由于有大量的读写操作,于是我们的 NAND Flash 制定了如下的读写规则:
那么问题来了!假如现在我要向磁盘中写入一张图片的数据,这个图片的数据大小刚好为一个 Page。最坏的情况就是,内存中恰好只有一个 Block 恰好有一个 Page 的无效数据可以擦除。为了存下这张图片,于是主控就把这个 Block 的所有数据读至缓存,擦除Block上的内容,再向缓存中加上这个4KB 新数据后最后写回 Block 中。
我的天啊,其实想存储的就是1个 Page 的图片内容,但是实际上确造成了整个 Block 的内容都被重新写入,同时原本简单一步搞定的事情被还被分成了前后四步执行(闪存读取、缓存改、闪存擦除、闪存写入)造成延迟大大增加,速度变慢。这就是传说中的“写入放大”(Write Amplification)问题。而“写入放大”也说明了磁盘在长期使用的过程中,其读写速度(尤其是写入速度)会存在降低的现象。
使用TRIM 技术解决 “写入放大”。
TRIM 是一条 ATA 指令,由操作系统发送给闪存主控制器,告诉它哪些数据占的地址是“无效”的。在 TRIM 的帮助下,闪存主控制器就可以提前知道哪些 Page 是“无效”的,便可以在适当的时机做出优化,从而改善性能。这里要强调下,TRIM 只是条指令,让操作系统告诉闪存主控制器这个 Page 已经“无效”就算完了,并没有任何其它多余的操作。在测试的过程中,我们发现 TRIM 的触发需要操作系统、驱动程序以及闪存主控三者都支持才能真正意义上实现。例如:
基于 TRIM 技术,目前常见有两种方案可以解决“写入放大”的问题:
1. discard 选项。该方案将在挂载 ext4 分区时加上 discard 选项,此后操作系统在执行每一个磁盘操作时同时都会执行 TRIM 指令。该方案的优点是总体耗时短,但影响会到删除文件时的性能。
2. fstrim 命令。该方案将选择合适的时机对整个分区执行 TRIM 操作。相对于方案一,该方案总体耗时较长,但不会影响正常操作时的磁盘性能。
不得不说,如果从用户的角度出发,还是 FSTRIM 的方法更靠谱一些,但如何寻找合适的 TRIM 时机就是一个比较讲究的问题了。
根据前面的分析,我们不难理解在 Android 中的 TRIM 选择通过 fstrim 命令的方式进行实现。那么,Google 又是如何设计触发TRIM的时机呢?
注释:
了解了这么多技术背景,那我们通过测试数据分析闪存碎片和 TRIM 对磁盘 I/O 性能的影响。根据测试目的,具体的测试设置如下:
评估闪存碎片和TRIM对磁盘 I/O 性能的影响
测试对象:LG Nexus 5 with cm-11-20140805-SNAPSHOT-M9-hammerhead
测试步骤:
1. 重新刷机,使用 Bonnie++ 测试 SD 卡目录的 I/O 性能;
2. 模拟长期使用 SD 卡的过程(期间需要避免TRIM触发),使用 Bonnie++ 测试 SD 卡目录的 I/O 性能;
3. 主动触发 TRIM,使用Bonnie++ 测试 SD 卡目录的 I/O 性能。
备注:
1. 模拟长期使用 SD 卡的过程的方法:开发专用的测试应用,该应用将向 SD 卡目录不停写入大小随机的文件,当 SD 卡剩余空间不足时将删除所写入的文件,然后继续上述操作直到应用退出。
2. 避免 TRIM 触发的方法:根据 Android 的触发过程分析,只需设置屏幕常亮并即可避免 TRIM 的触发。
1. 通过反复擦写 SD 卡,可以发现 SD 卡的 I/O 效率指标均存在一定幅度的下滑,其中反映磁盘空间分配性能及文件数据写回性能的指标下滑明显;
2. Sequential Output-Block 可以反映分配磁盘文件空间的效率,经反复擦写 SD 卡后,该效率降低至原始值的15-20%,应该是大量的磁盘闲置数据块造成的影响;
3. Sequential Output-Rewrite 可以反映文件系统缓存和数据传输的速度,经反复擦写 SD 卡制造闲置数据块后,该效率降低至原始值的50%。
4. 主动调用 TRIM 后,可以发现 SD 卡的 I/O 效率指标均恢复至接近原始值水平(但仍未完全达到初始状态的水平)。
1. 在 TRIM 无效的情况下,长期使用 SD 卡,磁盘写入速度会受到明显影响;
2. TRIM 对因闲置数据块造成的 I/O 性能下降有一定的恢复作用;
3. 大量的读写操作对 SD 卡造成了一定量的不可恢复的损耗。
完成了上面的工作,不由得让我们大吃一鲸:原来 TRIM 对 SD 卡的读写速度的维护如此重要!前面也说到,Android 选择 FSTRIM 方案的来实现 TRIM,那么 Android 所设计的 FSTRIM 触发时机有没有什么问题呢?
根据Android 系统的设定,FSTRIM 预期是每隔24小时触发一次。所以,接下来我们需要评估一下,FSTRIM 能否依据上述设定成功被系统触发。
分析FSTRIM 能否被按时被系统触发
测试对象:2台 Samsung Galaxy Nexus 及2台 LG Nexus 5
测试步骤:
1. 刷机后,安装常用应用并启动(均无SIM卡,其中1台设备开启 Wifi,另1台设备关闭 Wifi);
2. 进行 Log 记录;
3. 强制执行一次 FSTRIM;
4. 灭屏等待30小时左右,提取Log 记录进行分析。
开启WiFi
关闭WiFi
Samsung Galaxy Nexus
启动FSTRIM 1次
启动FSTRIM 1次
LG Nexus 5
未启动FSTRIM
启动FSTRIM 1次
据解读:
1. FSTRIM 大多数情况会被自动触发,但也存在无法触发的情况;
2. 根据 FSTRIM 的触发逻辑,是否开启 WIFI 对 FSTRIM 的影响主要是有无推送消息(影响灭屏条件)以及不同的耗电。
测试数据显示 FSTRIM 大多数情况会被自动触发,但也存在无法触发的情况。可能的原因是:FSTRIM对电量的要求略高,所以一旦发生意外情况(如应用的 PUSH 消息)终止了计划 FSTRIM 的执行之后,很长时间之内都无法再满足 FSTRIM 的启动条件。
所以,如需提高其触发频率,我们可以考虑降低触发条件中对电量的要求。
1. 用ARC管理内存
ARC(AutomaticReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露。它自动为你管理retain和release的过程,所以你就不必去手动干预了。忘掉代码段结尾的release简直像记得吃饭一样简单。而ARC会自动在底层为你做这些工作。除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存。
2. 在正确的地方使用 reuseIdentifier
一个开发中常见的错误就是没有给UITableViewCells, UICollectionViewCells,甚至是UITableViewHeaderFooterViews设置正确的reuseIdentifier。
为了性能最优化,table view用`tableView:cellForRowAtIndexPath:`为rows分配cells的时候,它的数据应该重用自UITableViewCell。一个table view维持一个队列的数据可重用的UITableViewCell对象。
不使用reuseIdentifier的话,每显示一行table view就不得不设置全新的cell。这对性能的影响可是相当大的,尤其会使app的滚动体验大打折扣。
自iOS6起,除了UICollectionView的cells和补充views,你也应该在header和footerviews中使用reuseIdentifiers。
想要使用reuseIdentifiers的话,在一个table view中添加一个新的cell时在data source object中添加这个方法:
staticNSString*CellIdentifier = @"Cell";
UITableViewCell*cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierforIndexPath:indexPath];
这个方法把那些已经存在的cell从队列中排除,或者在必要时使用先前注册的nib或者class创造新的cell。如果没有可重用的cell,你也没有注册一个class或者nib的话,这个方法返回nil。
3.尽量把views设置为透明
如果你有透明的Views你应该设置它们的opaque属性为YES。
原因是这会使系统用一个最优的方式渲染这些views。这个简单的属性在IB或者代码里都可以设定。
Apple的文档对于为图片设置透明属性的描述是:
(opaque)这个属性给渲染系统提供了一个如何处理这个view的提示。如果设为YES,渲染系统就认为这个view是完全不透明的,这使得渲染系统优化一些渲染过程和提高性能。如果设置为NO,渲染系统正常地和其它内容组成这个View。默认值是YES。
在相对比较静止的画面中,设置这个属性不会有太大影响。然而当这个view嵌在scroll view里边,或者是一个复杂动画的一部分,不设置这个属性的话会在很大程度上影响app的性能。
你可以在模拟器中用Debug\Color Blended Layers选项来发现哪些view没有被设置为opaque。目标就是,能设为opaque的就全设为opaque!
4.避免过于庞大的XIB
iOS5中加入的Storyboards(分镜)正在快速取代XIB。然而XIB在一些场景中仍然很有用。比如你的app需要适应iOS5之前的设备,或者你有一个自定义的可重用的view,你就不可避免地要用到他们。
如果你不得不XIB的话,使他们尽量简单。尝试为每个Controller配置一个单独的XIB,尽可能把一个View Controller的view层次结构分散到单独的XIB中去。
需要注意的是,当你加载一个XIB的时候所有内容都被放在了内存里,包括任何图片。如果有一个不会即刻用到的view,你这就是在浪费宝贵的内存资源了。Storyboards就是另一码事儿了,storyboard仅在需要时实例化一个view controller.
当你加载一个引用了图片或者声音资源的nib时,nib加载代码会把图片和声音文件写进内存。在OS X中,图片和声音资源被缓存在named cache中以便将来用到时获取。在iOS中,仅图片资源会被存进named caches。取决于你所在的平台,使用NSImage 或UIImage的`imageNamed:`方法来获取图片资源。
5.不要阻塞主线程
永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成。
一直使用主线程的风险就是如果你的代码真的block了主线程,你的app会失去反应。
大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络。
你可以使用`NSURLConnection`异步地做网络操作:
+(void)sendAsynchronousRequest:(NSURLRequest *)requestqueue:(NSOperationQueue*)queue completionHandler:(void (^)(NSURLResponse*,NSData*, NSError*))handler
或者使用像AFNetworking这样的框架来异步地做这些操作。
如果你需要做其它类型的需要耗费巨大资源的操作(比如时间敏感的计算或者存储读写)那就用Grand Central Dispatch,或者NSOperation和 NSOperationQueues.
下面代码是使用GCD的模板
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//switch to a background thread and perform your expensive operation
dispatch_async(dispatch_get_main_queue(),^{
//switch back to the main thread to update your UI
});
});
6. 在Image Views中调整图片大小
如果要在`UIImageView`中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。在运行中缩放图片是很耗费资源的,特别是`UIImageView`嵌套在`UIScrollView`中的情况下。
7. 选择正确的Collection
学会选择对业务场景最合适的类或者对象是写出能效高的代码的基础。当处理collections时这句话尤其正确。
一些常见collection的总结:
·Arrays: 有序的一组值。使用index来lookup很快,使用value lookup很慢,插入/删除很慢。
·Dictionaries: 存储键值对。用键来查找比较快。
·Sets: 无序的一组值。用值来查找很快,插入/删除很快。
8. 打开gzip压缩
大量app依赖于远端资源和第三方API,你可能会开发一个需要从远端下载XML, JSON, HTML或者其它格式的app。
减小文档的一个方式就是在服务端和你的app中打开gzip。这对于文字这种能有更高压缩率的数据来说会有更显著的效用。
好消息是,iOS已经在NSURLConnection中默认支持了gzip压缩,当然AFNetworking这些基于它的框架亦然。像Google App Engine这些云服务提供者也已经支持了压缩输出。
9. 重用和延迟加载(lazy load) Views
更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在UIScrollView里边的app更是如此。
这里我们用到的技巧就是模仿`UITableView`和`UICollectionView`的操作:不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。
这样的话你就只需要在滚动发生时创建你的views,避免了不划算的内存分配。
创建views的能效问题也适用于你app的其它方面。想象一下一个用户点击一个按钮的时候需要呈现一个view的场景。有两种实现方法:
1.创建并隐藏这个view当这个screen加载的时候,当需要时显示它;
2.当需要时才创建并展示。
每个方案都有其优缺点。用第一种方案的话因为你需要一开始就创建一个view并保持它直到不再使用,这就会更加消耗内存。然而这也会使你的app操作更敏感因为当用户点击按钮的时候它只需要改变一下这个view的可见性。
第二种方案则相反-消耗更少内存,但是会在点击按钮的时候比第一种稍显卡顿。
10. Cache
一个极好的原则就是,缓存所需要的,也就是那些不大可能改变但是需要经常读取的东西。
我们能缓存些什么呢?一些选项是,远端服务器的响应,图片,甚至计算结果,比如UITableView的行高。
NSURLConnection默认会缓存资源在内存或者存储中根据它所加载的HTTP Headers。你甚至可以手动创建一个NSURLRequest然后使它只加载缓存的值。
下面是一个可用的代码段,你可以可以用它去为一个基本不会改变的图片创建一个NSURLRequest并缓存它:
+(NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {
NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
[requestaddValue:@"image/*"forHTTPHeaderField:@"Accept"];
returnrequest;
注意你可以通过 NSURLConnection 获取一个URL request, AFNetworking也一样的。这样你就不必为采用这条tip而改变所有的networking代码了。
如果你需要缓存其它不是HTTP Request的东西,你可以用NSCache。
NSCache和NSDictionary类似,不同的是系统回收内存的时候它会自动删掉它的内容。
11.权衡渲染方法
在iOS中可以有很多方法做出漂亮的按钮。你可以用整幅的图片,可调大小的图片,uozhe可以用CALayer,CoreGraphics甚至OpenGL来画它们。
当然每个不同的解决方法都有不同的复杂程度和相应的性能。
简单来说,就是用事先渲染好的图片更快一些,因为如此一来iOS就免去了创建一个图片再画东西上去然后显示在屏幕上的程序。问题是你需要把所有你需要用到的图片放到app的bundle里面,这样就增加了体积–这就是使用可变大小的图片更好的地方了:你可以省去一些不必要的空间,也不需要再为不同的元素(比如按钮)来做不同的图。
然而,使用图片也意味着你失去了使用代码调整图片的机动性,你需要一遍又一遍不断地重做他们,这样就很浪费时间了,而且你如果要做一个动画效果,虽然每幅图只是一些细节的变化你就需要很多的图片造成bundle大小的不断增大。
总得来说,你需要权衡一下利弊,到底是要性能能还是要bundle保持合适的大小。
12.处理内存警告
一旦系统内存过低,iOS会通知所有运行中app。在官方文档中是这样记述:
如果你的app收到了内存警告,它就需要尽可能释放更多的内存。最佳方式是移除对缓存,图片object和其他一些可以重创建的objects的strong references.
幸运的是,UIKit提供了几种收集低内存警告的方法:
· 在appdelegate中使用`applicationDidReceiveMemoryWarning:`的方法
· 在你的自定义UIViewController的子类(subclass)中覆盖`didReceiveMemoryWarning`
· 注册并接收 UIApplicationDidReceiveMemoryWarningNotification的通知
一旦收到这类通知,你就需要释放任何不必要的内存使用。
例如,UIViewController的默认行为是移除一些不可见的view,它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的app可以移除不在屏幕上显示的图片。
这样对内存警报的处理是很必要的,若不重视,你的app就可能被系统杀掉。
然而,当你一定要确认你所选择的object是可以被重现创建的来释放内存。一定要在开发中用模拟器中的内存提醒模拟去测试一下。
13.重用大开销对象
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。
想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里或者创建静态变量来实现。
注意如果你要选择第二种方法,对象会在你的app运行时一直存在于内存中,和单例(singleton)很相似。
下面的代码说明了使用一个属性来延迟加载一个date formatter. 第一次调用时它会创建一个新的实例,以后的调用则将返回已经创建的实例:
//in your .h or inside a class extension
@property(nonatomic, strong) NSDateFormatter *formatter;
//inside the implementation (.m)
-(NSDateFormatter *)formatter {
if(!_formatter) {
_formatter= [[NSDateFormatter alloc] init];
return_formatter;
还需要注意的是,其实设置一个NSDateFormatter的速度差不多是和创建新的一样慢的!所以如果你的app需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升。
14. 使用Sprite Sheets
Spritesheet可以让渲染速度加快,甚至比标准的屏幕渲染方法节省内存。
15.避免反复处理数据
许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要。在内存中操作数据使它们满足你的数据结构是开销很大的。
比如你需要数据来展示一个table view,最好直接从服务器取array结构的数据以避免额外的中间数据结构改变。
类似的,如果需要从特定key中取数据,那么就使用键值对的dictionary。
16.选择正确的数据格式
从app和网络服务间传输数据有很多方案,最常见的就是JSON和XML。你需要选择对你的app来说最合适的一个。
解析JSON会比XML更快一些,JSON也通常更小更便于传输。从iOS5起有了官方内建的JSON deserialization就更加方便使用了。
但是XML也有XML的好处,比如使用SAX来解析XML就像解析本地文件一样,你不需像解析json一样等到整个文档下载完成才开始解析。当你处理很大的数据的时候就会极大地减低内存消耗和增加性能。
17.正确设定背景图片
使用UIColor的 colorWithPatternImage来设置背景色;
在view中添加一个UIImageView作为一个子View。
如果你使用全画幅的背景图,你就必须使用UIImageView因为UIColor的colorWithPatternImage是用来创建小的重复的图片作为背景的。这种情形下使用UIImageView可以节约不少的内存:
//You could also achieve the same result in Interface Builder
UIImageView*backgroundView = [[UIImageView alloc] initWithImage:[UIImageimageNamed:@"background"]];
如果你用小图平铺来创建背景,你就需要用UIColor的colorWithPatternImage来做了,它会更快地渲染也不会花费很多内存:
18. 减少使用Web特性
UIWebView很有用,用它来展示网页内容或者创建UIKit很难做到的动画效果是很简单的一件事。
但是你可能有注意到UIWebView并不像驱动Safari的那么快。这是由于以JIT compilation为特色的Webkit的Nitro Engine的限制。
所以想要更高的性能你就要调整下你的HTML了。第一件要做的事就是尽可能移除不必要的javascript,避免使用过大的框架。能只用原生js就更好了。
另外,尽可能异步加载例如用户行为统计script这种不影响页面表达的javascript。
最后,永远要注意你使用的图片,保证图片的符合你使用的大小。使用Sprite sheet提高加载速度和节约内存。
19. 设定Shadow Path
如何在一个View或者一个layer上加一个shadow呢,QuartzCore框架是很多开发者的选择:
#import
//Somewhere later ...
UIView*view = [[UIView alloc] init];
//Setup the shadow ...
看起来很简单,对吧。可是,坏消息是使用这个方法也有它的问题… Core Animation不得不先在后台得出你的图形并加好阴影然后才渲染,这开销是很大的。
使用shadowPath的话就避免了这个问题:
使用shadowpath的话iOS就不必每次都计算如何渲染,它使用一个预先计算好的路径。但问题是自己计算path的话可能在某些View中比较困难,且每当view的frame变化的时候你都需要去updateshadow path.
20. 优化Table View
Tableview需要有很好的滚动性能,不然用户会在滚动过程中发现动画的瑕疵。
为了保证tableview平滑滚动,确保你采取了以下的措施:
· 正确使用`reuseIdentifier`来重用cells
· 尽量使所有的view opaque,包括cell自身
· 避免渐变,图片缩放,后台选人
· 缓存行高
· 如果cell内现实的内容来自web,使用异步加载,缓存请求结果
· 使用`shadowPath`来画阴影
· 减少subviews的数量
· 尽量不适用`cellForRowAtIndexPath:`,如果你需要用到它,只用一次然后缓存结果
· 使用正确的数据结构来存储数据
· 使用`rowHeight`,`sectionFooterHeight`和 `sectionHeaderHeight`来设定固定的高,不要请求delegate
21.选择正确的数据存储选项
当存储大块数据时你会怎么做?
你有很多选择,比如:
· 使用`NSUerDefaults`
· 使用XML,JSON, 或者 plist
· 使用NSCoding存档
· 使用Core Data
NSUserDefaults的问题是什么?虽然它很nice也很便捷,但是它只适用于小数据,比如一些简单的布尔型的设置选项,再大点你就要考虑其它方式了
XML这种结构化档案呢?总体来说,你需要读取整个文件到内存里去解析,这样是很不经济的。使用SAX又是一个很麻烦的事情。
NSCoding?不幸的是,它也需要读写文件,所以也有以上问题。
在这种应用场景下,使用SQLite 或者 Core Data比较好。使用这些技术你用特定的查询语句就能只加载你需要的对象。
在性能层面来讲,SQLite和Core Data是很相似的。他们的不同在于具体使用方法。Core Data代表一个对象的graph model,但SQLite就是一个DBMS。Apple在一般情况下建议使用Core Data,但是如果你有理由不使用它,那么就去使用更加底层的SQLite吧。
23. 使用Autorelease Pool
`NSAutoreleasePool`负责释放block中的autoreleased objects。一般情况下它会自动被UIKit调用。但是有些状况下你也需要手动去创建它。
假如你创建很多临时对象,你会发现内存一直在减少直到这些对象被release的时候。这是因为只有当UIKit用光了autorelease pool的时候memory才会被释放。好消息是你可以在你自己的@autoreleasepool里创建临时的对象来避免这个行为:
NSArray*urls = <# An array of file URLs #>;
for(NSURL*url in urls) {
@autoreleasepool{
NSError*error;
NSString*fileContents = [NSString stringWithContentsOfURL:urlencoding:NSUTF8StringEncoding error:&error];
/*Process the string, creating and autoreleasing more objects. */
这段代码在每次遍历后释放所有autorelease对象
24. 选择是否缓存图片
常见的从bundle中加载图片的方式有两种,一个是用`imageNamed`,二是用`imageWithContentsOfFile`,第一种比较常见一点。
既然有两种类似的方法来实现相同的目的,那么他们之间的差别是什么呢?
相反的,`imageWithContentsOfFile`仅加载图片。
下面的代码说明了这两种方法的用法:
UIImage*img = [UIImage imageNamed:@"myImage"];// caching
//or
UIImage*img = [UIImage imageWithContentsOfFile:@"myImage"];// no caching
那么我们应该如何选择呢?
如果你要加载一个大图片而且是一次性使用,那么就没必要缓存这个图片,用`imageWithContentsOfFile`足矣,这样不会浪费内存来缓存它。
然而,在图片反复重用的情况下`imageNamed`是一个好得多的选择。
25. 避免日期格式转换
如果你要用`NSDateFormatter`来处理很多日期格式,应该小心以待。就像先前提到的,任何时候重用`NSDateFormatters`都是一个好的实践。
嗯,直接用C来搞,看起来不错了,但是你相信吗,我们还有更好的方案!
-(NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return[NSDatedateWithTimeIntervalSince1970:timestamp];
这样会比用C来解析日期字符串还快!需要注意的是,许多web API会以微秒的形式返回时间戳,因为这种格式在javascript中更方便使用。记住用`dateFromUnixTimestamp`之前除以1000就好了。
CPU跟踪模板为您提供的方法来确定你的应用程序如何使用多个核心,你使用多少能源和其他资源的测量。
性能监视器计数器(pmc)硬件寄存器,衡量事件发生在处理器。 他们可以用来帮助识别瓶颈在您的应用程序通过识别过度数量的一个特定类型的事件。 例如,大量的条件分支指令可能表明一段逻辑,如果重新安排,可能会降低所需数量的分支。 尽管PMC事件可以带来这些问题,这是由你来匹配您的代码和决定他们将帮助你提高你的应用程序的性能。
在工具中,您使用计数器仪器跟踪PMC事件。
跟踪PMC事件计数器乐器
1. 打开计数器跟踪模板。
2. 单击检查员按钮计数器的乐器。
3. 点击加号按钮(+)。
4. 单击事件改变到另一个事件。
PMC事件列表填充一组初始的常见的事件。 您可以添加特定于您的应用程序通过窗口菜单的事件。
添加新的PMC事件
1. 选择窗口>管理点事件。
2. 单击状态对应于所需的事件并选择可见或最喜欢的。
重要的是: PMC事件的数量,可以跟踪是依赖于硬件的。 试图跟踪太多的事件可能会导致一个错误。 试验设置来确定事件的数量,可以成功地跟踪一次。
事件分析器工具跟踪性能监视器中断(PMI)事件,但在这种情况下,只有一个事件跟踪,你可以得到更多的细节。 设置每个收集的样本集的大小事件分析器工具。 事件分析器然后为您提供信息如何积极跟踪PMI事件是在样例。 使用跟踪这些样品显示窗格中滑块识别大量的活动。 确定高活动区域后,用细节窗格中获得更多的信息在每个特定的例子。
添加新的PMI事件
1. 选择Window >管理点事件。
2. 单击状态对应于事件和选择可见或最喜欢的。
3. 阈值的字段中,输入一个事件的次数必须发生在一个示例。
能源诊断跟踪模板提供了关于能源使用的诊断,以及基本的开/关状态主要设备组件。 该模板由能源使用、CPU活动,网络活动,显示亮度,睡眠/唤醒,蓝牙,无线网络,和GPS仪器。
能源诊断日志记录,你的iOS设备记录与能源相关的数据不同时使用的设备。 因为日志记录是有效的,你可以记录所有的一天。 日志持续而iOS设备处于睡眠模式,但是如果设备电池干燥或iOS设备电源关闭,运行日志数据丢失。
开发人员设置的发展似乎只有在设备供应。 设置设备重启后消失。 Xcode恢复设置通过连接装置或仪器。
跟踪能源使用iOS设备上
2. 以用户角度去使用调试您的APP。
3. 获取数据后,关掉开发日志。
减少你的应用程序使用的能量通过确保你关掉Recode。 您可以验证如果你有关闭特定的无线电使用能源诊断跟踪模板。 每个广播与红色描述跟踪和黑色面板中指定,指定它是关闭的。
多核跟踪模板分析多核性能,包括线程状态、调度队列,并阻止使用。 它是调度线程工具。
线程状态工具为您提供了一个图形表示的每个线程状态在特定时间运行。 每个块用不同颜色来帮助你识别每个线程。 经过多个线程状态改变很容易通过改变颜色的识别跟踪窗格。 图7显示了四个线程被跟踪。
图7线程活动显示的线程状态跟踪模板
查看应用程序的线程使用
1. 选择多核跟踪模板。
2. 运行您的应用程序。
3. 通过选择复选框选择线程来检查标记列在细节窗格中。
以下跟踪中捕获操作窗格。 颜色符号是默认颜色为每个行动,但他们可以改变你。
随着线程状态仪器,多核模板包含派遣仪器。 使用调度工具来查看当你执行调度队列。 你可以看到分派线程能持续多久和多少块。
CPU策略的时间分析器工具显示了应用程序如何利用多核。 选择CPU战略跟踪文档中的配置跟踪窗格中显示的时间在x轴和y轴上的处理器核心。 CPU策略使用视图有助于比较核心的使用在给定的时间段。 有效的核心并发提高应用程序的性能。 重使用单个核心而其他领域核心保持安静可以描述区域需要更大的优化。
查看单个核心的使用
2. 选择你的应用从弹出菜单中选择目标。
3. 点击记录,锻炼你的应用程序,然后单击停止捕捉数据。
4. 单击CPU策略按钮。
5. 选择使用。
6. 寻找核心使用不平衡。
确保您的应用程序是使用多个核心同时通过跟踪窗格的放大。 很快一个或两个线程之间跳转的核心可以让它像时同时使用多个核心在现实中,只有一个核心是使用在任何时候。
本章重点1、了解软件测试复杂性与经济性2、掌握软件测试的阶段3、掌握软件测试的方法4、掌握软件测试的分类5、理解常见软件测试过程模型一、软件测试复杂性与经济性软件测试的复杂性(1)、完全测试是不现实的(2)、软件测试是有风险的(3)、杀虫剂现象(4)、缺陷的不确定性软件测试的经济性测试费用除了测试的直接消耗外,还包括其它的相关费用。测试费用的主要因素有:(1)、软件面向
系统可能在频繁的出现Full GC。Full GC是Java 应用程序垃圾回收的一种机制,一般如果出现了Full GC,应用程序就会出现短暂的停顿。关于Full GC的介绍,可以参考纸质书5.1.7小节中的介绍。此时可以先去看一下应用程序的GC日志,如果是Full GC 非常频繁,并且又没有出现内存泄漏,那么可以参考纸质书5.4.1 小节中介绍的如何减少GC 来解决这个问题。《软件性能测试分析与调优实践之路》(第2版) 读书笔记
简介性能测试计划是在进行软件或系统的性能测试之前制定的详细计划和指导文件。它描述了所需性能测试的目标、范围、测试环境、资源需求、测试策略、测试用例、时间表等重要信息。为什么要制定性能测试计划制定性能测试计划的主要目的是确保性能测试的有效性和可靠性。以下是制定性能测试计划的重要原因:明确测试目标:性能测试计划可以明确定义所需测试的性能目标,例如响应时间、吞吐量、并发用户数等。这有助于确保测试的准确性
第一章 软件性能测试基本概念和流程1.1 软件性能的定义通常来说,性能首先是一种指标,表明软件系统或构件对其即时性要求的符合程度;
概念&目的本周分享的性能测试,主要面向的是服务端的性能测试。性能测试是从业务中提取压测模型,然后利用压测工具按照模型制造压测流量,并对目标应用集群进行施压,在施压过程中观察应用集群的性能表现和发掘性能瓶颈的测试行为。当前性能测试主要分为线上压测和线下压测。线上压测主要通过全链路压测执行,线下压测是单机(或者小集群)压测。全链路压测请看全链路压测章节,本节主要讨论通用性能测试方案和流程。常用指标1. 虚拟用户虚拟用户,模拟真实业务逻辑步骤的虚拟用户,虚拟用户模拟的操作步..
误区5:在开发环境下进行一下性能测试就可以了很多时候,在系统开发完成后会进行性能测试,在开发环境“看一看”软件的性能。而实际上大多数的开发环境因为硬件条件比较差,所以反映不了系统的真正性能。这是因为系统的性能表现通常依赖于硬件,一些系统在资源不足的情况下可能运行缓慢,这类问题往往提高一下硬件配置就可以解决问题。一些系统在资源不足条件下发现的性能问题,主要通过优化程序来解决,则可能需要花费非常高的人
性能测试
Iometer测试它可以用来衡量出一个存储子系统实际工作的效能,它的主要参数是IOps,每秒进行的操作数。 可以看出,在几乎所有的随机情况中,RAID5性能都比单硬盘要好;在几乎所有的连续情况中,RAID5性能都和单硬盘差不多。例外的就是写入情况,RAID
软件测试目的 软件测试目的 1)确保软件质量 2)减少质量问题给企业及用户带来隐患 测试分类 测试分类 1)安装测试 2)构建测试 3)白盒测试 4)黑盒测试 5)性能测试 6)迁移测试 …… 单元测试 单元测试 1、定义:开发人员针对程序模块(软件设计的最小单位)来进行正确性检验的测试; 2、单元
4.2并发登录用户测试 测试内容: 这次测试没有加入思考时间(think time),只是登陆页面的响应。 说明:用户的整个执行流程都录制在Action(循环)部分,所以Vuser_int (开始)和Vuser_end(结束)部分为空。Action_Transaction部分的时间为运行整个Acti
我们用电脑的每个人,不都是
测试活动的开展测试计划 测试活动的目的和被测目标 测试的范围 测试的方法 测试所用到的资源 测试的进度安排 测试的策略测试用例的产生
如果文章哪里有错误描述或表达错误,还请各位指出来,欢迎点赞,欢迎评论什么是性能使用来描述产品除功能外的所具有的速度,效率和能力的综合能力评价什么是性能测试性能测试是通过自动化的测试工具模拟多种正常,峰值以及异常负载条件来对系统的各项性能指标进行测试性能测试的目的识别系统的弱点,评估系统给能力,发现系统性能瓶颈,提高系统的稳定性和可靠性性能测试内容性能测试通过包括一下九个方面性能测试:表示在给定的基
1.什么是软件测试,以及软件测试的意义?软件测试是软件开发过程的重要组成部分,是用来确认一个程序的品质或性能是否符合开发之前所提出的一些要求。软件测试就是在软件投入运行前,对软件需求分析、设计规格说明和编码的最终复审,是软件质量保证的关键步骤。软件测试是为了发现错误而执行程序的过程。软件测试在软件生存期中横跨两个阶段:通常在编写出每一个模块之后就对它做必要的测试(称为单元测试)。编码和单元测试属于
我们一提到性能测试,很多人就直接连想到Loadrunner。认为LR就等于性能测试,其实这是不对的。LR只是性能测试的一个工具,但性能测试不仅仅是LR。一、软件性能的定义软件的性能是软件的一种非功能特性,它关注的不是软件是否能够完成特定的功能,而是在完成该功能时展示出来的及时性。性能测试是指通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。针对与我们测试人员,关
十六个推荐的性能测试工具在软件测试日常工作中,大家接触得比较多的性能测试工具有LoadRunner和Jmeter,这里整理了web应用程序性能和负载压力能力的最广泛使用的性能测试工具的综合列表。这些负载测试工具将确保您的应用程序在高峰流量和极端压力条件下的性能。该列表包括开源的以及授权的性能测试工具。但是几乎所有的授权工具都有一个免费的试用版本,这样您就可以在决定哪种工具最适合您的需求之前有机会亲
软件性能测试概述1.什么是软件性能测试?性能测试是指通过特定方式,对被测系统按照一定策略施加压力,获取系统响应
Chapter 1_软件测试概述 **软件测试的IEEE定义:**使用人工或自动的手段来运行或测量软件系统的过程,目的是检验软件系统是否满足规定的需求,并找出与预期结果之间的差异。 软件测试的发展趋势: ① 测试工作将进一步前移。软件测试不仅仅是单元测试、集成测试、系统测试和验收测试,还对需求的精确 ...
在Unreal Engine 4(UE4)中,蓝图系统允许开发者通过可视化编程创建游戏逻辑和功能,而无需写一行代码。电梯系统作为游戏中常见的交互元素,通过蓝图可以简单直观地构建电梯的运行逻辑、楼层控制和用户交互等功能。电梯楼层指示灯是电梯系统中不可或缺的一部分,它对乘客提供实时信息和指引,增加乘坐过程的透明度。通过上述方法,可以有效地实现和优化电梯楼层指示灯的逻辑和功能。在下一章节中,我们将深入探讨楼层选择按钮的功能和实现方式。
概述稍微对并发源码了解的朋友都知道,很多并发工具如ReentrantLock、CountdownLatch的实现都是依赖AQS, 全称AbstractQueuedSynchronizer。AQS是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。一般来说,同步工具实现锁的控制分为独占锁和共享锁,而AQS提供了对这两种模式的支持。独占锁: 也叫排他锁,即锁只能由一个线程获取,若
本文详细介绍了在Ubuntu系统中管理MySQL的关键操作,包括:1. 修改认证方式(兼容MySQL5.7/8.0版本差异);2. 创建远程用户并配置权限;3. 修改的两种场景(已知和忘记);4. 服务状态监控与启停命令;5. 设置开机自启;6. 常用用户/数据库/日志管理操作;7. 安全注意事项(如复杂度、防火墙配置等)。文章特别强调了版本差异带来的操作区别,并提供了生产环境下的安全建议,如避免root远程登录、定期备份等重要实践。
从MongoDB到国产多模数据库的平替,福建电子证照系统的实践证明:政务系统的国产化升级并非“牺牲性能换安全”,而是通过“场景化技术适配”实现“自主可控+效能提升”的双赢。金仓多模数据库的核心价值,在于其既能兼容文档数据库的灵活性,又能满足政务数据的规范性与高并发需求,为“数字”建设提供了坚实的底层支撑。对于更多正在推进国产化改造的政务项目而言,“从业务痛点出发,选择适配场景的技术方案”才是关键——毕竟,国产化的最终目标,从来不是“替换工具”,而是“更好地支撑政务服务”。
随着互联网技术的飞速发展,数字内容的可访问性问题日益受到。特别是在公共信息服务平台、网站和教育系统中,确保残障用户能够平等获取信息已成为一项基本的技术伦理要求。在此背景下,基于WCAG 1.0标准构建的开源自动化检测工具——CAC(Comprehensive Accessibility Checker)应运而生。WCAG 1.0由W3C组织发布,确立了“可感知、可操作、可理解、可兼容”四大核心原则,并划分A、AA、AAA三级合规等级,为网页无障碍提供了基础评估框架。