zoukankan      html  css  js  c++  java
  • 【Java/Android性能优3】Android性能调优工具TraceView使用介绍

    本文转自:http://blog.csdn.net/innost/article/details/9008691

    在软件开发过程中,想必很多读者都遇到过系统性能问题。而解决系统性能问题的几个主要步骤是:

    • 测评:对系统进行大量有针对性的测试,以得到合适的测试数据。
    • 分析系统瓶颈:分析测试数据,找到其中的hotspot(热点,即bottleneck)。
    • 性能优化:对hotspot相关的代码进行优化。

    由上述步骤可知,性能优化的目标对象是hotspot。如果找到的hotspot并非真正的热点,则性能优化的结果必然是事倍功半甚至竹篮打水一场空。所以,作为Android性能调优相关知识的第一部分,本篇首先将向读者介绍Android平台中三个重要的性能测试工具,它们能很好得帮助开发者找到hotspot。

     

    Traceview介绍

    1.1  Traceview简介

    Traceview是Android平台特有的数据采集和分析工具,它主要用于分析Android中应用程序的hotspot。Traceview本身只是一个数据分析工具,而数据的采集则需要使用Android SDK中的Debug类或者利用DDMS工具。二者的用法如下:

    • 开 发者在一些关键代码段开始前调用Android SDK中Debug类的startMethodTracing函数,并在关键代码段结束前调用stopMethodTracing函数。这两个函数运行过 程中将采集运行时间内该应用所有线程(注意,只能是Java线程)的函数执行情况,并将采集数据保存到/mnt/sdcard/下的一个文件中。开发者然 后需要利用SDK中的Traceview工具来分析这些数据。
    • 借助Android SDK中的DDMS工具。DDMS可采集系统中某个正在运行的进程的函数调用信息。对开发者而言,此方法适用于没有目标应用源代码的情况。DDMS工具中Traceview的使用如图1-1所示。

    1-1  DDMS中Traceview使用示意图

    点击图1-1中所示按钮即可以采集目标进程的数据。当停止采集时,DDMS会自动触发Traceview工具来浏览采集数据。

    下面,我们通过一个示例程序向读者介绍Debug类以及Traceview的使用。

    1.2  Traceview示例分析

    示例程序运行时的界面如图1-2所示:

    1-2  示例界面图

    1-2中:

    • SystraceDemoStringAAA等字样是TraceviewDemo程序启动时ListView最初显示的字符串。
    • 当用户点击ListView中的某一项时,Traceview将计算对应项当前显示的字符串的MD5值40次,然后用计算得到的MD5字符串替换该项之前显示的内容。其效果如图1-2中的“MD5值“箭头所示。

    该示例的关键代码如图1-3所示:

    1-3示例代码

    由图1-3可知:

    • 左图中,Debug类的startMethodTracing和stopMethodTracing分别在MainAcvtivity的构造方法和onDestroy函数中调用。
    • onCreate函数中我们设置了第一个hotspot,即getStringToShow函数。它将解析一个XML文件,并将解析后的字符串保存到mListItem中以作为ListView的显示内容。
    • 右图中,当用户点击ListView中的某个Item时,程序在onListItem中将计算MD5值40次,然后用计算结果做为被点击项的新字符串显示。generateMD5中的函数是本示例的第二个hotspot。

    现在,我们用Traceview工具将测试结果文件TraceviewDemo.trace打开。

    Traceview界面比较复杂,其UI划分为上下两个面板,即Timeline Panel(时间线面板)和Profile Panel(分析面板)。图1-4所示为Timeline Panel界面:

    1-4  Traceview Timeline Panel示意图

    1-4中的Timeline Panel又可细分为左右两个Pane:

    • 左边Pane显示的是测试数据中所采集的线程信息。由图1-4可知,本次测试数据采集了main线程,两个Binder线程和其它系统辅助线程(例如GC线程等)的信息。
    • 右边Pane所示为时间线,时间线上是每个线程测试时间段内所涉及的函数调用信息。这些信息包括函数名、函数执行时间等。由图1-4可知,main线程对应行的的内容非常丰富,而其他线程在这段时间内干得工作则要少得多。
    • 另外,开发者可以在时间线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。
    • 一类是那些自身占用时间不长,但调用却非常频繁的函数。在示例代码中,它就是hotspot 2。

    首先,我们来查找hotspot 1。

    Profile Panel中,选择按Cpu Time/Call进行降序排序(从上之下排列,每项的耗费时间由高到低),得到如图1-6所示的结果:

    1-6  按CPU Time/Call降序排列数据

    1-6中:

    • MainActivity.onCreate是应用程序中的函数,它耗时为4618.684。然后,点击MainActivity.onCreate项,得到箭头所示的小图。
    • 小图中,Parents一行显示的是MainActivity.onCreate的调用者,本例中它是performCreate函数。这部分代码属于Framework部分。Children行显示的是MainActivity.onCreate调用的子函数。
    • 在 MainActivity.onCreate调用的子函数中,我们发现getStringsToShow在Incl Cpu Time %一列中占据了63.3%,它是onCreate子函数耗费时间最长的,而且Calls+Recur Calls/Total列显示其调用次数为1,即它仅仅被调用一次了。这个函数是应用程序实现的,所以极有可能是一个潜在的Hotspot。
    • 另外,由于笔者已经知道getStringsToShow是示例应用自己实现的函数,故在图1-6的大图中,可直接根据MainActivity.getStringsToShow花费了2921.913CPU时间这个信息来确定Hotspot就是它。

    相对来说,类型1的hotspot比较好找,步骤是先按降序对时间项进行排列(可以是时间百分比、真实时间或CPU时间),然后查找耗费时间最多的函数。一般而言,先应对应用程序自己实现的函数进行排查,Framework的函数也有可能是hotspot,但主因一般还是在应用本身(例如设置复杂的界面,导致对应XML解析非常慢)。

    现在,我们来看如何查找类型2的hotspot。

    点击Call/Recur Calls/Total列头,使之按降序排列。关注点放在那些调用频繁并且占用资源较多的函数。图1-7为降序排列的结果图。

    1-7类型2 Hotspot查找过程示意之一

    1-7所示的运行最频繁的几个函数中,我们发现了几个怀疑点,由图中的1和2箭头标示。

    • 结 合代码,箭头1所指的函数在代码中实际并不存在。这是因为代码中直接访问了内部类的私有成员,导致java编译器在编译时自动生成了这个函数。这个函数的 调用次数非常多。所以,为了提高效率,我们可以修改内部类成员的访问类型定义为public。不过,该函数的Incl Cpu Time并不高,只有3.2%。
    • 同样,箭头2所指部分的函数调用次数也很多,达到了5888多次。不过它们占用的时间百分比只有0.9%。

    第一次查找的潜在点被排除后,继续浏览数据,得到如图1-8所示的结果。

    1-8  类型2 Hotspot查找过程示意之二

    在图1-8中:

    • 红框处有两个重载的MyMD5.getHashString函数调用,它们各运行了368次,而且占用的CPU时间百分比达到了31.8%和53.2%。很显然,这2处调用就有优化的余地,这就是我们所怀疑的hotspot2。

    找到hotspot之后,开发者就需要结合代码来进行对应的优化了。关于Java代码优化,读者可参考如下资料:http://developer.android.com/training/articles/perf-tips.html

    总体而言,Hotspot的查找是一个细致的工作,需要开发者对目标程序的代码,以及Traceview工具都比较熟悉才行。

    1.3  Traceview小结

    Traceview工具是Android平台应用程序性能分析的利器。不过笔者觉得它的UI还是有些复杂。并且使用时感觉流畅度不够好。

    Google官方关于Traceview的介绍可参考以下链接,不过其内容以及较久未更新了。http://developer.android.com/tools/debugging/debugging-tracing.html。

     

    Systrace介绍

    2.1  Systrace简介

    Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统(如surfaceflinger、WindowManagerService等Framework部分关键模块、服务)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。

    Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中,它主要由3部分组成:

    • 内核部分:Systrace利用了Linux Kernel中的ftrace功能。所以,如果要使用Systrace的话,必须开启kernel中和ftrace相关的模块。
    • 数据采集部分:Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。
    • 数 据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集 ftrace统计数据并生成一个结果网页文件供用户查看。

    从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace。Android 4.1为系统中的几个关键进程和模块都添加了Systrace功能。以显示系统中重要模块Hwcomposer为例,其代码中使用Systrace的方法如图2-1所示:

     

    2-1  Hwcomposer模块Systrace使用示例

    2-1中,应用程序只要通过三个宏就可使用Systrace了:

    • 定义ATRACE_TAG:Hwcomposer使用了ATRACE_TAG_GRAPHICS,表示它和Graphics相关。
    • ATRACE_INIT:用于统计某个变量使用的情况。下文将见到代码中”VSYNC”的统计结果。
    • ATRACE_CALL:用于统计函数的调用情况。

    由于篇幅关系,关于Trace使用更多的信息请读者阅读frameworks/native/include/utils/Trace.h或者android.os.Trace类。下面,我们通过一个示例来展示Systrace的使用。

    2.2  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非常类似。图中包含的内容如下:

    • 由 于在systrace.py中指定了-f -l和-i参数,Systrace将生成CPU频率、负载和状态相关的信息。它们为图2-1中第一个红框所示。由于笔者所测手机CPU为双核,故图中有 CPU 0和CPU 1之分。为行文方便,笔者用CPU N来指代CPU的某个核。
    • “CPU N“所示行对应于整个测试时间内,某个核上运行的进程信息。
    • “CPU N C-State“所示行为整个测试时间内,某个CPU状态的变化。C-State取值见表2-1。
    • “CPU N Clock Frequency”所示行展示了某个CPU运行的频率。通过点击这一行的色块可以查看某个时间点上CPU N的运行频率。
    • “cpufreq”: 该行所示内容和CPU交互式频率调节器(Interactive Governor)的工作有关。交互式CPU调节器驱动添加了对CPU频率调节事件的跟踪。感兴趣的读者不妨阅读kernel中的 include/trace/events/cpufreq_interactive.h文件以了解更多的信息。

    2-1中,CPU信息以下的行就是通过Trace.h提供的宏而添加的统计信息,其中:

    • VSYNC:这一行的统计信息来自于图2-1中ATRACE_INIT宏的使用。在Hwcomposer代码中,ATRACE_INIT宏被用于统计VSYNC[1]的Tick-Tack情况(即0,1,0,1交错输出)。VSYNC行显示了每次Tick Tack的时间大概都在16ms左右。
    • 由 于Framework代码也在显示部分添加了ATRACE_INIT的使用,所以图中 com.example.systracedemo/com.example.systracedemo.MainActivity所示为应用程序占用显 示Buffer的Tick-Tack情况。如果使用时间超过16ms,将导致界面显示迟滞等现象。
    • SurfaceFlinger使用了ATRACE_CALL宏,故图中SurfaceFlinger行展示了其函数调用的CPU耗时情况(如箭头1所指,SurfaceFlinger中的onMessageReceived函数的运行信息)。
    • 在图2-1最下部的方框中,详细显示了当前鼠标在时间线中选择的部分(即SurfaceFlinger中的onMessageReceived)的详细信息。

    2-1所示为CPU状态取值信息:

    2-1  CPU状态

    C-state

    描述

    C-0

    RUN MODE,运行模式。

    C-1

    STANDBY,就位模式,随时准备投入运行

    C-2

    DORMANT,休眠状态,被唤醒投入运行时有一定的延迟

    C-3

    SHUTDOWN,关闭状态,需要有较长的延迟才能进入运行状态,减少耗电

    2.3  Systrace小结

    总体来说,Systrace比Traceview用途更广泛,它支持对CPU、Native进程甚至Kernel线程进行性能数据采样,可帮助开发者对整个系统的性能情况进行一个详尽的分析。不过其用法比Traceview要复杂,而且还需要对Kernel做一些配置调整。

    Android官方对Systrace也有一些介绍,请读者阅读:

    http://developer.android.com/tools/debugging/systrace.html

    Oprofile的使用

    3.1  Oprofile简介

    Oprofile是另一个功能更强大的性能数据采集和分析工具,其工作原理如下:

    • 它利用性能计数器(Performance Counter)或者定时器(针对kernel不支持性能计数器的情况),通过连续的采样获得统计数据,从而对内核和用户空间进程进行性能分析。
    • 以 性能计数器为例,在系统运行过程中,当某个事件发生时,对应的性能计数器就会自加。当达到计数器的设定值时会产生一个中断。Oprofile驱动利用这个 中断来进行采样统计。通过获取中断发生时PC指针的值以及内核中保存运行的任务的信息等,并把它们转化成对测评有用的数据。
    • Oprofile包括内核驱动和用户空间工具两个部分,其中:
    • 内 核驱动实现了一个oprofilefs虚拟文件系统。它挂载到/dev/oprofile,用来向用户空间报告数据和接收来自用户空间的设置。它是用户空 间进程与内核通信的桥梁。驱动中还包括了与架构相关和通用的驱动,通过它们访问性能计数器寄存器、收集数据后报告给用户空间。守护进程用户从内核接收数据 并保存在磁盘上以备分析使用。
    • 在用户空间提供了两个工具:oprofiled(作为守护进程在后台通过和/dev/oprofile交互以获取驱动收集的数据)、opcontrol(用户操作的控制工具,它通过读写oprofilefs来控制采样相关的设置)。

    Android默认提供了对Oprofile的支持,其组成包括:

    • 代码:位于exetrnal/oprofile中。不过,只有编译类型为非user的系统才会使用它。
    • 四个主要工具,即opcontrol,oprofiled、opreport和opimport。开发者只要使用opcontrol和opreport即可。
    • 读者应该熟练掌握opcontrol和oprofiled工具的作用,我们此处也总结了它们的用法:
    • opcontrol:它用来控制采样过程,比如采样的开始和结束、采样的事件类型和频率等。其内部通过读写oprofilefs来实现。opcontrol的常用选项如表3-1所示:

    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:是否采样用户事件

    • opreport:opreport 是使用采样数据生成报告的工具,可根据用户要求生成不同的报告。一般用法是“opreport [options] [image]”,其中image指定报告需要显示的程序的名字(指程序名字、共享库名字和内核)。image参数可选。不指定它时,opreport将 打印所有进程的报告结果。常用options如表3-2所示:

    3-2  opreport常用选项

    opreprt选项

    功能

    -l

    显示函数调用的符号名字

    -g

    以调试的形式打印函数符号,包括函数所在文件及行数等。

    -c

    显示函数调用堆栈

    -o

    报告输出到指定文件

    另外,Android提供了一个特别的工具opimport_pull。它可把采样数据从手机中pull到PC上,并对数据进行一些简单处理以供opreport使用。所以,在Android平台上,开发者只要使用opimport_pull了就可以了。

    现在,我们来看Oprofile的使用实例。

    3.2  Oprofile实例

    Oprofile的使用大体可以分成以下三步:

    • 内核加载oprofile驱动(如果该驱动静态编译到内核中,则可略过此步骤)。
    • 配置采样事件、然后进行采样。
    • 获取报告、进行分析,针对分析结果进行改进。

    下面分别来看这三个步骤:

    3.2.1  Oprofile内核配置

    如下所示为内核配置的示例,如图3-1所示:

    3-1  Oprofile内核配置示意

    运行Oprofile需要root权限,所以目标设备中最好运行的是userdebug或者engineer版本的Android OS。

    3.2.2  Oprofile用户空间配置

    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.2.3  结果分析

    在上一步中,我们已经获取了测评采样的数据。现在,就可以使用它们来生成采样报告了,方法如图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()后的测试结果

    3.3  Oprofile小结

    在性能分析中,Oprofile无疑是一个使用最广泛、功能最强大的测评工具。对于Android平台开发者来说,它可以采集和分析整个系统的运行状态信息,对于分析查找系统瓶颈进而优化系统具有重大意义。

     

      总结

    性能调优向来是一件“高深莫测”的任务,但打破它们神秘面纱的工具就是上文所述的工具了。所以,对有志开展这方面工作的读者而言,首要一步的工作就是先了解各个工具的作用及优缺点。

    除了本文介绍的这三个工具外,Android系统还支持其他一些更有针对性的测试工具,例如用于测评系统整体功能的lmbench,lttng、测试系统启动性能的bootchart、测试文件系统性能的iozone等。由于篇幅关系,笔者就不再一一介绍它们了。

     [1]关于VSYNC的详情,读者可参考http://blog.csdn.net/innost/article/details/8272867,“Android Project Butter分析“一文。

  • 相关阅读:
    HUST 1372 marshmallow
    HUST 1371 Emergency relief
    CodeForces 629D Babaei and Birthday Cake
    CodeForces 629C Famil Door and Brackets
    ZOJ 3872 Beauty of Array
    ZOJ 3870 Team Formation
    HDU 5631 Rikka with Graph
    HDU 5630 Rikka with Chess
    CodeForces 626D Jerry's Protest
    【POJ 1964】 City Game
  • 原文地址:https://www.cnblogs.com/dongdong230/p/4200641.html
Copyright © 2011-2022 走看看