性能收集分析相关工具总览
收集、分析、展示移动应用性能数据的工具很多,大致可以分为如下几类。例如可收集多项性能指标的移动性能工具,perfdog,Solopi,其中Solopi开源,pefdog商业工具。可进行Crash分析的工具,例如商业的Firebase Crashlytics和开源的Sentry工具。如果需要对网络相关数据进行分析,可以使用Charles或者Wireshark。除此之外,还有综合性的性能管理工具,例如Dynatrace等。总体来说,商业工具能力,准确性,用户体验肯定强于开源工具,作为个人学习研究,更多只有从开源工具出发。所以,此篇博客在编写中,也会以开源工具进行举例。
移动端性能测试工具:
PerfDog:用于监控应用性能、CPU、内存、电量消耗等数据的工具,支持移动应用的性能测试和实时监控。
Solopi:专注于移动应用性能的测试和监控,包括帧率、CPU、内存等指标的实时监测。
Crash分析工具:
Firebase Crashlytics:Google提供的强大的崩溃报告和分析工具,可实时监控应用的崩溃情况。
Sentry:开源的错误追踪工具,支持多种平台,包括移动应用的错误报告和性能监控。
网络性能测试工具:
Charles Proxy:网络调试代理工具,可以捕获和分析移动应用的网络请求和响应数据。
Wireshark:开源的网络分析工具,支持捕获和分析移动应用的网络数据包。
APM工具(应用性能管理):
Dynatrace:云原生的全栈性能监控解决方案,支持移动应用性能监控和用户体验分析。
收集哪些性能指标以及如何收集
进行性能分析前,首先要明白每个性能指标含义,以solopi收集的性能指标为例,我们来展开看看移动应用性能常见指标,以及可以通过哪些方式收集这些指标。
帧率
帧率的计算公式是:帧率=绘制的帧数/时间段,大多数应用程序和游戏开发者的目标帧率是60FPS(帧/每秒),为什么目标帧率是60FPS呢?因为大多数现代智能手机和平板电脑的屏幕刷新率为60Hz,所以,只要帧率达到60FPS,即每秒绘制的帧数是60,那么用户视觉效果就比较流畅,不会有卡顿的感觉。最低不能低于30FPS,这是因为显示器在等待新的帧时,重复显示上一帧的图像,导致画面不流畅。高的可以达到90FPS(例如,对于高刷新频率的设备而言)。
如何收集帧率信息
收集帧率的方式有两种,一种是利用Choreographer类统计帧率,这种需要在应用内部写代码实现,只能收集该应用的帧率信息。另外一种方式是使用gfxinfo工具。以Solopi为例,也是通过gfxinfo工具信息计算1秒内超时帧时间,从而反推出实际帧率,所以在接近静止的情况下, 部分帧率可能显示有误。推荐在滑动或页面切换等动态场景下进行帧率测试。gfxinfo的方式,可以收集所有应用的帧率信息,非常适合做性能数据收集工具,所以,这里,也重点介绍如何通过gfxinfo收集帧率信息。
收集帧率信息主要有两个步骤,步骤一:通过adb命令获取gfxinfo信息,步骤二:对信息进行解析,计算得出帧率信息。gfxinfo信息内容大致如下所示:
Stats since: 统计开始时间,单位为纳秒。
Total frames rendered: 总渲染帧数。
Janky frames: 卡顿帧数,即超过16ms(每秒60帧的刷新间隔)的帧数。
Janky frames percentage: 卡顿帧占比,即卡顿帧数与总帧数的比例。
90th percentile: 90分位数,表示90%的帧的渲染时间低于该值(以毫秒为单位)。
95th percentile: 95分位数,表示95%的帧的渲染时间低于该值(以毫秒为单位)。
99th percentile: 99分位数,表示99%的帧的渲染时间低于该值(以毫秒为单位)。
Number Missed Vsync: 未同步垂直同步次数,即由于未能及时渲染而导致的丢帧次数。
Number High input latency: 高输入延迟次数,表示输入事件处理时间过长而导致的延迟次数。
Number Slow UI thread: UI线程响应缓慢次数,即UI线程处理时间过长的次数。
以Solopi为例子,就是通过gfxinfo信息中的超时帧时间,来反推帧率的,具体项目的代码可以查看solopi source code。
卡顿率/卡顿次数
卡顿次数:在测试周期内,检测到帧率低于阈值的时间段数。每当帧率低于阈值并持续一段时间,就计为一次卡顿。这里的阀值,可以进行自定义设置,例如低于目标帧率60FPS,就可以记为一次卡顿。即帧渲染时间大于16ms,就计算为卡顿。
卡顿率:卡顿时间占总测试时间的百分比。假设在一次测试中,应用运行了120秒,期间总共有4次帧率低于16 FPS的情况,总计卡顿时间为8秒,那么:卡顿次数:4次。卡顿率:8/120*100%=6.7%。在上面的gfxinfo中,也有卡顿率的数据信息。
cpu/内存
以solopi为例,cpu是包含应用顶层Activity所在进程的CPU占用百分比与全局CPU占用百分比,对于单进程应用,该数据表示该应用的CPU占用情况;对于多进程进程应用,该数据表示顶层UI进程的CPU占用情况,当发生进程切换时,Soloπ能够自动切换到新的进程数据。
import subprocessdef get_memory_info(pid):try:# Run adb shell command to read /proc/<pid>/statmcommand = f"adb shell cat /proc/{pid}/statm"result = subprocess.check_output(command, shell=True)statm_data = result.decode('utf-8').strip().split()# Parse statm datasize, resident, shared, text, lib, data, dt = map(int, statm_data)memory_info = {'size': size, # total program size (pages)'resident': resident, # resident set size (pages)'shared': shared, # shared pages (pages)'text': text, # text (code) size (pages)'lib': lib, # library (unused since Linux 2.6; always 0)'data': data, # data + stack (pages)'dt': dt # dirty pages (unused since Linux 2.6; always 0)}return memory_infoexcept subprocess.CalledProcessError as e:print(f"Error executing command: {e}")return Noneexcept Exception as e:print(f"Error: {e}")return None# Replace with your application's PID
pid = '12345'
memory_info = get_memory_info(pid)
if memory_info:print(f"Memory info for PID {pid}:")print(f"Size: {memory_info['size']} pages")print(f"Resident set size (RSS): {memory_info['resident']} pages")print(f"Shared pages: {memory_info['shared']} pages")print(f"Text (code) size: {memory_info['text']} pages")print(f"Data + stack size: {memory_info['data']} pages")
网络
以solopi为例,网络包含应用上下行速率与累计流量以及全局上下行速率与累计流量。属于应用维度数据,具体数据如下图所示:
如何获取网络数据?
获取网络数据和获取cpu/内存数据一样,可以直接读取/proc/pid/net/dev文件获取网络数据,或者通过adb命令获取(命令:adb shell cat /proc/pid/net/dev)。下图是solopi部分源码,可以看到这里统计了wlan0的网络数据情况,具体逻辑可以查看solopi source code下面的NetworkTools.java代码。当然,关于网络流量统计,网上也提交了bug,因为这里grep wlan0,只统计了网络wifi的流量,没有统计到移动流量。bug详细信息见这里。
响应时间
以solopi为例,包含应用点击的响应耗时与刷新耗时数据。属于应用维度数据。从用户点击开始,到系统第一次发出界面更新时间为响应耗时,到系统停止界面刷新的时间为刷新耗时。统计响应时间的逻辑是录屏分帧,solopi官网介绍是实现了自动识别开始帧和结束帧来统计响应时间,实际使用solopi会发现,统计的页面响应时间并不正确,例如用户点击开始,到点击应用的页面,这中间本身有人的手速操作时间在内,这部分被统计到响应时间里面了。所以,如果要计算准备的页面响应时间,还是只有录屏,人工识别页面的开始、结束帧,会更准确。具体录屏分帧的步骤如下所示:
以上就是对于移动应用中常见性能指标含义理解,以及如何收集详细说明。关于crash等,会在后续博客中再详细介绍。