经做者受权,发表Tieto某青年牛的一篇《程序员》大做。 html
在软件开发过程当中,想必不少读者都遇到过系统性能问题。而解决系统性能问题的几个主要步骤是: java
由上述步骤可知,性能优化的目标对象是hotspot。若是找到的hotspot并不是真正的热点,则性能优化的结果必然是事倍功半甚至竹篮打水一场空。因此,做为Android性能调优相关知识的第一部分,本篇首先将向读者介绍Android平台中三个重要的性能测试工具,它们能很好得帮助开发者找到hotspot。 python
Traceview是Android平台特有的数据采集和分析工具,它主要用于分析Android中应用程序的hotspot。Traceview自己只是一个数据分析工具,而数据的采集则须要使用Android SDK中的Debug类或者利用DDMS工具。两者的用法以下: linux
图1-1 DDMS中Traceview使用示意图 android
点击图1-1中所示按钮便可以采集目标进程的数据。当中止采集时,DDMS会自动触发Traceview工具来浏览采集数据。 程序员
下面,咱们经过一个示例程序向读者介绍Debug类以及Traceview的使用。 浏览器
示例程序运行时的界面如图1-2所示: 性能优化
图1-2 示例界面图 架构
图1-2中: composer
该示例的关键代码如图1-3所示:
图1-3示例代码
由图1-3可知:
如今,咱们用Traceview工具将测试结果文件TraceviewDemo.trace打开。
Traceview界面比较复杂,其UI划分为上下两个面板,即Timeline Panel(时间线面板)和Profile Panel(分析面板)。图1-4所示为Timeline Panel界面:
图1-4 Traceview Timeline Panel示意图
图1-4中的Timeline Panel又可细分为左右两个Pane:
如今来看Traceview的Profile Panel界面,如图1-5所示:
图1-5 TraceviewProfile Panel界面
Profile Panel是Traceview的核心界面,其内涵很是丰富。它主要展现了某个线程(先在Timeline Panel中选择线程)中各个函数调用的状况,包括CPU使用时间、调用次数等信息。而这些信息正是查找hotspot的关键依据。因此,对开发者而言,必定要了解Profile Panel中各列的含义。笔者总结了其中几个重要列的做用,如表1-1所示:
表1-1 Profile Panel各列做用说明
列名 |
描述 |
Name |
该线程运行过程当中所调用的函数名 |
Incl Cpu Time |
某函数占用的CPU时间,包含内部调用其它函数的CPU时间 |
Excl Cpu Time |
某函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间 |
Incl Real Time |
某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间 |
Excl Real Time |
某函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间 |
Call+Recur Calls/Total |
某函数被调用次数以及递归调用占总调用次数的百分比 |
Cpu Time/Call |
某函数调用CPU时间与调用次数的比。至关于该函数平均执行时间 |
Real Time/Call |
同CPU Time/Call相似,只不过统计单位换成了真实时间 |
另外,每个Time列还对应有一个用时间百分比来统计的列(如Incl Cpu Time列对应还有一个列名为Incl Cpu Time %的列,表示以时间百分比来统计的Incl Cpu Time)。
了解完Traceview的UI后,如今介绍如何利用Traceview来查找hotspot。
通常而言,hotspot包括两种类型的函数:
首先,咱们来查找hotspot 1。
在Profile Panel中,选择按Cpu Time/Call进行降序排序(从上之下排列,每项的耗费时间由高到低),获得如图1-6所示的结果:
图1-6 按CPU Time/Call降序排列数据
图1-6中:
相对来讲,类型1的hotspot比较好找,步骤是先按降序对时间项进行排列(能够是时间百分比、真实时间或CPU时间),而后查找耗费时间最多的函数。通常而言,先应对应用程序本身实现的函数进行排查,Framework的函数也有多是hotspot,但主因通常仍是在应用自己(例如设置复杂的界面,致使对应XML解析很是慢)。
如今,咱们来看如何查找类型2的hotspot。
点击Call/Recur Calls/Total列头,使之按降序排列。关注点放在那些调用频繁而且占用资源较多的函数。图1-7为降序排列的结果图。
图1-7类型2 Hotspot查找过程示意之一
图1-7所示的运行最频繁的几个函数中,咱们发现了几个怀疑点,由图中的1和2箭头标示。
第一次查找的潜在点被排除后,继续浏览数据,获得如图1-8所示的结果。
图1-8 类型2 Hotspot查找过程示意之二
在图1-8中:
找到hotspot以后,开发者就须要结合代码来进行对应的优化了。关于Java代码优化,读者可参考以下资料:http://developer.android.com/training/articles/perf-tips.html
整体而言,Hotspot的查找是一个细致的工做,须要开发者对目标程序的代码,以及Traceview工具都比较熟悉才行。
Traceview工具是Android平台应用程序性能分析的利器。不过笔者以为它的UI仍是有些复杂。而且使用时感受流畅度不够好。
Google官方关于Traceview的介绍可参考如下连接,不过其内容以及较久未更新了。http://developer.android.com/tools/debugging/debugging-tracing.html。
Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统(如surfaceflinger、WindowManagerService等Framework部分关键模块、服务)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。
Systrace的功能包括跟踪系统的I/O操做、内核工做队列、CPU负载以及Android各个子系统的运行情况等。在Android平台中,它主要由3部分组成:
从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程须要利用Android提供的Trace类来使用Systrace。Android 4.1为系统中的几个关键进程和模块都添加了Systrace功能。以显示系统中重要模块Hwcomposer为例,其代码中使用Systrace的方法如图2-1所示:
图2-1 Hwcomposer模块Systrace使用示例
图2-1中,应用程序只要经过三个宏就可以使用Systrace了:
因为篇幅关系,关于Trace使用更多的信息请读者阅读frameworks/native/include/utils/Trace.h或者android.os.Trace类。下面,咱们经过一个示例来展现Systrace的使用。
首先,在PC机上运行以下命令以启动Systrace,如图2-2所示:
图2-2 Systrace操做步骤
执行上述命令后,将获得一个名为trace.html的文件(trace.html是默认文件名,读者也可在命令行中指定其余文件名)。经过浏览器打开此文件,结果如图2-3所示:
图 2-3 trace.html内容示意
图2-3中所示的trace.html页面内容和Traceview的Timeline Panel很是相似。图中包含的内容以下:
图2-1中,CPU信息如下的行就是经过Trace.h提供的宏而添加的统计信息,其中:
表2-1所示为CPU状态取值信息:
表2-1 CPU状态
C-state |
描述 |
C-0 |
RUN MODE,运行模式。 |
C-1 |
STANDBY,就位模式,随时准备投入运行 |
C-2 |
DORMANT,休眠状态,被唤醒投入运行时有必定的延迟 |
C-3 |
SHUTDOWN,关闭状态,须要有较长的延迟才能进入运行状态,减小耗电 |
整体来讲,Systrace比Traceview用途更普遍,它支持对CPU、Native进程甚至Kernel线程进行性能数据采样,可帮助开发者对整个系统的性能状况进行一个详尽的分析。不过其用法比Traceview要复杂,并且还须要对Kernel作一些配置调整。
Android官方对Systrace也有一些介绍,请读者阅读:
http://developer.android.com/tools/debugging/systrace.html
Oprofile是另外一个功能更强大的性能数据采集和分析工具,其工做原理以下:
Android默认提供了对Oprofile的支持,其组成包括:
表3-1 opcontrol经常使用选项
opcontrol选项 |
功能 |
--list-events |
列出当前CPU所支持的事件 |
--setup |
对测评进行设置,好比关闭旧的守护进程、挂载oprofilefs |
--vmlinux= |
设置将要分析的Android内核镜像文件 |
--callgraph |
设置跟踪函数调用的层数 |
--kernel-range=start,end |
内核二进制文件起始和结束的虚拟地址 |
--start/--stop |
开始/中止采样 |
--event=name:count:unitmask:kernel:user |
设置对某事件进行采样。 Name:事件的名字 Count:采样时事件发生的次数Unitmask:事件的掩码(CPU支持的事件以及掩码见oprofile的文档) Kernel:是否采样内核事件 User:是否采样用户事件 |
表3-2 opreport经常使用选项
opreprt选项 |
功能 |
-l |
显示函数调用的符号名字 |
-g |
以调试的形式打印函数符号,包括函数所在文件及行数等。 |
-c |
显示函数调用堆栈 |
-o |
报告输出到指定文件 |
另外,Android提供了一个特别的工具opimport_pull。它可把采样数据从手机中pull到PC上,并对数据进行一些简单处理以供opreport使用。因此,在Android平台上,开发者只要使用opimport_pull了就能够了。
如今,咱们来看Oprofile的使用实例。
Oprofile的使用大致能够分红如下三步:
下面分别来看这三个步骤:
以下所示为内核配置的示例,如图3-1所示:
图3-1 Oprofile内核配置示意
运行Oprofile须要root权限,因此目标设备中最好运行的是userdebug或者engineer版本的Android OS。
Oprofile用户空间配置的示例如图3-2所示。假设当前目录为Android源码根目录,而且已经初始化Android编译环境(执行完毕build/envsetup.sh和lunch)。
图3-2 Oprofile用户空间配置示意
用户空间的配置主要经过执行opcontrol命令来完成。而opcontrol内部是经过往oprofilefs传递对应的控制参数来完成的。例如图3-2中“opcontrol --callgraph=16”命令也可经过“echo 16> /dev/oprofile/backtrace_depth”来实现。
在上一步中,咱们已经获取了测评采样的数据。如今,就可使用它们来生成采样报告了,方法如图3-3所示:
图3-3 oprofile生成采样报告方法示意
图3-4为报告的一部份内容:
图3-4 Oprofile测评报告概要
图3-4中,咱们发现libc.so调用的采样数为117299,排第4位。那么libc.so中哪一个函数调用次数最多呢?开发者可经过以下命令获取libc.so的更为详细的信息。方法如图3-5所示:
图3-5 opreport使用示例
执行上述命令后的结果如图3-6所示:
图3-6 Oprofile关于libc的详细结果
由图3-6可知,memcpy()函数占用最多的CPU资源。因此能够考虑优化memcpy()。
此处,笔者针对Cortex-A9双核SMP处理器,使用ARM汇编的方法对memcpy进行了优化。优化后的结果如图3-7所示。对比图3-6和图3-7可明显看出,优化后的memcpy对资源的占用下降了2.7个百分点。
图3-7 优化memcpy()后的测试结果
在性能分析中,Oprofile无疑是一个使用最普遍、功能最强大的测评工具。对于Android平台开发者来讲,它能够采集和分析整个系统的运行状态信息,对于分析查找系统瓶颈进而优化系统具备重大意义。
性能调优向来是一件“高深莫测”的任务,但打破它们神秘面纱的工具就是上文所述的工具了。因此,对有志开展这方面工做的读者而言,首要一步的工做就是先了解各个工具的做用及优缺点。
除了本文介绍的这三个工具外,Android系统还支持其余一些更有针对性的测试工具,例如用于测评系统总体功能的lmbench,lttng、测试系统启动性能的bootchart、测试文件系统性能的iozone等。因为篇幅关系,笔者就再也不一一介绍它们了。
[1]关于VSYNC的详情,读者可参考http://blog.csdn.net/innost/article/details/8272867,“Android Project Butter分析“一文。