移动性能测试 Android FPS 方法探讨

花开 · October 25, 2019 · Last by morou replied at April 08, 2024 · 2879 hits

最近在学习 Android 稳定性测试相关的知识点,目前主要参考xinxi 的 持续集成中的 Android 稳定性测试, 但是在看到FPS相关指标的数据的时候,我发现了一些不一样的地方,然后发现社区多数的计算方法都是来自这一篇文章 FPS 计算方法的比较, 然后我就产生了疑问?我的数据是这样的:

环境

  • 木木摸机器
  • 系统 6.0
  • 测试 app 雪球
Applications Graphics Acceleration Info:
Uptime: 17498916 Realtime: 17498916

** Graphics info for pid 1306 [com.xueqiu.android] **

Stats since: 1014402977980ns
Total frames rendered: 15148
Janky frames: 4108 (27.12%)
90th percentile: 18ms
95th percentile: 22ms
99th percentile: 44ms
Number Missed Vsync: 215
Number High input latency: 9
Number Slow UI thread: 634
Number Slow bitmap uploads: 27
Number Slow issue draw commands: 3106

Caches:
Current memory usage / total memory usage (bytes):
  TextureCache         23542452 / 25165824
  LayerCache              32768 / 16777216 (numLayers = 1)
    Layer size 128x64; isTextureLayer()=0; texid=943 fbo=0; refs=1
  Layers total      32768 (numLayers = 1)
  RenderBufferCache           0 /  2097152
  GradientCache           16384 /   524288
  PathCache              198843 /  4194304
  TessellationCache      266880 /  1048576
  TextDropShadowCache         0 /  2097152
  PatchCache               4032 /   131072
  FontRenderer 0 A8     1048576 /  1048576

  FontRenderer 0 RGBA         0 /        0
  FontRenderer 0 total  1048576 /  1048576
Other:

  FboCache                    0 /        0
Total memory usage:
  25109935 bytes, 23.95 MB
Profile data in ms:
    com.xueqiu.android/com.xueqiu.android.common.MainActivity/android.view.ViewRootImpl@9e9e9b8 (visibility=0)
    Draw    Prepare Process Execute
    11.53   0.46    4.68    2.23
    3.96    0.26    4.53    9.07
    4.08    0.63    4.67    8.99
    3.45    0.48    2.48    11.92
    3.62    0.52    3.48    9.18
    5.41    0.83    4.04    3.37
    2.43    0.22    2.88    2.62
    2.14    0.26    2.80    2.04
    2.15    0.36    4.85    2.27
    3.25    0.32    3.29    2.02
    3.05    0.33    3.95    2.07
    2.24    0.22    8.27    3.37
    2.62    0.27    7.20    3.09
    1.52    0.19    3.27    1.34
    2.65    0.17    6.58    1.32
    2.60    0.12    2.71    4.74
    1.43    0.17    3.08    1.15
    2.32    0.26    2.66    1.76
    5.19    0.16    2.59    1.61


Stats since: 1014402977980ns
Total frames rendered: 15148
Janky frames: 4108 (27.12%)
90th percentile: 18ms
95th percentile: 22ms
99th percentile: 44ms
Number Missed Vsync: 215
Number High input latency: 9
Number Slow UI thread: 634
Number Slow bitmap uploads: 27
Number Slow issue draw commands: 3106
View hierarchy:

  com.xueqiu.android/com.xueqiu.android.common.MainActivity/android.view.ViewRootImpl@9e9e9b8

  629 views, 586.51 kB of display lists


Total ViewRootImpl: 1

Total Views:        629

Total DisplayList:  586.51 kB

测试代码如下:

cmd = "adb  shell dumpsys gfxinfo %s" % ('com.xueqiu.android')
result = os.popen(cmd).read().strip()
frames = [x for x in result.split('\n') if validator(x)]
for frame in frames:
    time_block = re.split(r'\s+', frame.strip())

    if len(time_block) == 3:
        print (time_block)

结果

['Applications', 'Graphics', 'Acceleration', 'Info:']
['Uptime:', '17838571', 'Realtime:', '17838571']
['Total', 'frames', 'rendered:', '15951']       
['Janky', 'frames:', '4375', '(27.43%)']        
['Number', 'Missed', 'Vsync:', '216']
['TextureCache', '23546664', '/', '25165824']   
['RenderBufferCache', '0', '/', '2097152']      
['GradientCache', '16384', '/', '524288']       
['PathCache', '198843', '/', '4194304']
['TessellationCache', '266880', '/', '1048576']
['TextDropShadowCache', '0', '/', '2097152']
['PatchCache', '4032', '/', '131072']
['FboCache', '0', '/', '0']
['25114147', 'bytes,', '23.95', 'MB']
['Profile', 'data', 'in', 'ms:']
['Draw', 'Prepare', 'Process', 'Execute']
['13.13', '0.54', '5.32', '2.91']
['4.10', '0.26', '4.73', '7.49']
['4.51', '1.13', '5.86', '6.09']
['3.01', '0.51', '3.24', '10.94']
['2.37', '1.42', '4.53', '8.74']
['1.78', '0.54', '4.79', '10.27']
['1.93', '0.78', '2.76', '12.70']
['2.52', '0.28', '2.43', '12.89']
['2.03', '0.57', '2.37', '13.17']
['2.10', '0.26', '2.37', '12.42']
['1.83', '0.47', '2.76', '12.40']
['1.42', '0.46', '2.90', '12.35']
['1.19', '0.25', '2.83', '12.86']
['1.32', '0.40', '1.92', '13.32']
['4.32', '0.68', '2.16', '10.04']
['16.73', '0.63', '4.38', '4.26']
['4.80', '0.38', '4.32', '4.00']
['3.37', '0.45', '4.69', '8.29']
['2.60', '0.53', '8.96', '5.25']
['2.90', '1.98', '7.99', '5.82']
['2.84', '0.46', '5.11', '8.96']
['1.54', '0.62', '3.27', '12.16']
['1.91', '0.29', '3.20', '12.85']
['2.60', '0.36', '4.50', '9.75']
['1.69', '1.24', '3.32', '11.90']
['2.17', '0.50', '2.28', '13.18']
['2.34', '0.37', '3.42', '11.96']

问题

  • 目前输出数据是 4 列,所以这段代码是无效的,获取不到 fps 数据
  • 修改判断条件为 if len(time_block) == 4:, 出现很多无效的数据,虽然可以通过 try,catch 捕获,但是正常数据里面有了脏数据,这些数据计算结果应该是 60fps

修复

通过正则获取目标数据

cmd = "adb  shell dumpsys gfxinfo %s" % ('com.xueqiu.android')
result = os.popen(cmd).read().strip()
data = re.findall(r'(?<=Execute)([\w\W]+)(?=Stats)', result)
frames = [x for x in data[0].split('\n') if validator(x)]
for frame in frames:
    time_block = re.split(r'\s+', frame.strip())

    if len(time_block) == 4:
        print (time_block)

待解决问题

  • 通过返回数据判断出是三列还是四列,这个我相信大家都是可以做到的

这个是我觉得挺好的,分享一下

最佳回复

根据我的实际经验来看,FPS 的数值其实意义不大,反而是 janky rate 的意义大于 FPS。

直接以 dumpsys gfxinfo 命令获取的 jankey rate 数值来评价 app 的流畅度吧。

就先用最简单的FPS为例,用它来评估流畅度就有问题,例如用户掉了6帧,将近100ms,用户是能感受到卡顿的,但是FPS却是54FPS,这是个相当好的分数,在WWDC里面都赞的分数。因此应该用janky,也就是掉帧本身来定制指标,才能反映用户的卡顿。

具体可以参考这篇文章:https://www.jianshu.com/p/56775aa366cb

FPS 只是数据,最终是要筛出问题提供给研发解决,这里的问题就是度量数据如何用?单从实现 fps 度量的方案上:
16 年我设计了 (https://testerhome.com/topics/4775)
今年又设计了 (https://testerhome.com/topics/21045)
可实际问题上是用它们如何去解决问题,能解决什么问题?

1)要将需要研发解决的问题聚焦在什么程度线上?这就需要了解一般用户的体验需求
a、人眼有 100ms 延迟,一些人经过训练或长期适应高帧率场景会有卡顿敏感度提升
b、竞速类游戏有限制 30 帧的做法,核心是人眼对变化感知明显,所以有些场景控制帧率稳定比提高帧率有效果
c、视频及体验是从 24 帧电影录制及网络流视频 25 帧视频来的
d、20 帧体验,作为一般帧率影响体验的游戏,20 帧就是个底线了,就像 ui 上的 gif 动画一般是 20 帧播放,至少这是被人接受的

2)要和配合优化的研发达成一致的优化目标,要不实际会浪费在优化意义的争论上

3)说说我的经验中,用我自己的方案解决了哪些实际问题
a、手机 rom 游戏效果优化,目标:让游戏帧率稳定,通过策略调整看帧率波动。实际优化是温升限频策略、续航、游戏性能间找平衡点,带着目标做事才能有的放矢。
b、TV 项目 costdown,低端芯片 mstar 648 1.5G 内存项目,我给的目标,内存要给前台 app 提供至少 500M 可用内存,最终通过 bsp、rom、系统 app 各层面的努力,配合后台查杀策略提供了 800M 左右可用内存;而帧率上通过单帧绘制 jank 比例发现 SurfaceFlinger 绘制性能有问题,结果通过研发优化思路探索,打开 Gop 的 overlap 实现减少一次 io 复制,使 SurfaceFlinger 绘制性能明显改善。
c、通过 16 年的方案分场景检查静止画面下,SurfaceFlinger 的无实际意义绘制,控制了重复绘制图层、不关闭未在显示中的 gif 动画绘制两大类不合理性能开销的问题
d、基于流畅度评价,70 分以上每 10 分跨度都可以感受到体验区别,从而把桌面插件上线标准控制在 70 分以上,目标 80 分以上的标准,有效把控了插件上线的流畅度体验。

基于这些应该能理解吧,不在于如何把数据拿到,最核心的是如何用。我扩展的抓取方式只是为了提供拿到数据的手段。

共收到 11 条回复 时间 点赞

之前测试 FPS 的时候,参考了 @sandman 的这篇文章:https://testerhome.com/topics/4775
我个人评估了一下,觉得这个比较准确

我记得我以前的方式是修改 Choreographer 的一个成员变量 xxx_LIMIT,本来默认是 30,改成 1,然后 log 就会输出所有的丢帧数据,再根据时间戳,计算同一秒内的丢帧

simple 回复

嗯,这个我也看了,昨天太晚了就没有尝试

rhyme 回复

我回头看看源代码,这样就需要修改代码了吧?还是我可以启动另一个程序来做这个操作的?

根据我的实际经验来看,FPS 的数值其实意义不大,反而是 janky rate 的意义大于 FPS。

直接以 dumpsys gfxinfo 命令获取的 jankey rate 数值来评价 app 的流畅度吧。

就先用最简单的FPS为例,用它来评估流畅度就有问题,例如用户掉了6帧,将近100ms,用户是能感受到卡顿的,但是FPS却是54FPS,这是个相当好的分数,在WWDC里面都赞的分数。因此应该用janky,也就是掉帧本身来定制指标,才能反映用户的卡顿。

具体可以参考这篇文章:https://www.jianshu.com/p/56775aa366cb

Joo 回复

学习了👍

FPS 只是数据,最终是要筛出问题提供给研发解决,这里的问题就是度量数据如何用?单从实现 fps 度量的方案上:
16 年我设计了 (https://testerhome.com/topics/4775)
今年又设计了 (https://testerhome.com/topics/21045)
可实际问题上是用它们如何去解决问题,能解决什么问题?

1)要将需要研发解决的问题聚焦在什么程度线上?这就需要了解一般用户的体验需求
a、人眼有 100ms 延迟,一些人经过训练或长期适应高帧率场景会有卡顿敏感度提升
b、竞速类游戏有限制 30 帧的做法,核心是人眼对变化感知明显,所以有些场景控制帧率稳定比提高帧率有效果
c、视频及体验是从 24 帧电影录制及网络流视频 25 帧视频来的
d、20 帧体验,作为一般帧率影响体验的游戏,20 帧就是个底线了,就像 ui 上的 gif 动画一般是 20 帧播放,至少这是被人接受的

2)要和配合优化的研发达成一致的优化目标,要不实际会浪费在优化意义的争论上

3)说说我的经验中,用我自己的方案解决了哪些实际问题
a、手机 rom 游戏效果优化,目标:让游戏帧率稳定,通过策略调整看帧率波动。实际优化是温升限频策略、续航、游戏性能间找平衡点,带着目标做事才能有的放矢。
b、TV 项目 costdown,低端芯片 mstar 648 1.5G 内存项目,我给的目标,内存要给前台 app 提供至少 500M 可用内存,最终通过 bsp、rom、系统 app 各层面的努力,配合后台查杀策略提供了 800M 左右可用内存;而帧率上通过单帧绘制 jank 比例发现 SurfaceFlinger 绘制性能有问题,结果通过研发优化思路探索,打开 Gop 的 overlap 实现减少一次 io 复制,使 SurfaceFlinger 绘制性能明显改善。
c、通过 16 年的方案分场景检查静止画面下,SurfaceFlinger 的无实际意义绘制,控制了重复绘制图层、不关闭未在显示中的 gif 动画绘制两大类不合理性能开销的问题
d、基于流畅度评价,70 分以上每 10 分跨度都可以感受到体验区别,从而把桌面插件上线标准控制在 70 分以上,目标 80 分以上的标准,有效把控了插件上线的流畅度体验。

基于这些应该能理解吧,不在于如何把数据拿到,最核心的是如何用。我扩展的抓取方式只是为了提供拿到数据的手段。

这个适配性不好整,目前我用华为 p30 来执行 adb 命令,返回的数据并没有 Stats 这个关键字,我看你正则是用 Stats 来判断的结尾,所以在我这数据又不对了


既然有这两个值,那不是每秒获取一次,然后计算差值就可以了吗?

'Draw', 'Prepare', 'Process', 'Execute']
['13.13', '0.54', '5.32', '2.91']
['4.10', '0.26', '4.73', '7.49']
['4.51', '1.13', '5.86', '6.09']
['3.01', '0.51', '3.24', '10.94']
['2.37', '1.42', '4.53', '8.74']
['1.78', '0.54', '4.79', '10.27']
['1.93', '0.78', '2.76', '12.70']
['2.52', '0.28', '2.43', '12.89']
['2.03', '0.57', '2.37', '13.17']

这部分数据没有时间戳,那第二次会不会重复获取到?

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up