iOS性能优化之耗电检测

前言

  • 如果某个App耗电量过大会导致手机发烫,会导致电池寿命变短,会导致用户体验不好。
  • 本文给出Energy impact、Energy Log、sysdiagnose三种耗电检测方案,帮你找到耗电的代码,帮你提高用户体验

Energy impact

准备

  • Edit Scheme->Profile->Build Configuration下拉设置为Debug,如果没有配置点击Instruments中的红色按钮运行iPhone的时候是会报错的
  • Build Settings->Build Options->Debug Information Format->Debug->DWARF with dSYM File,如果没有配置检测结果显示地址而不是函数名。
  • 设置完成后选择Product->Profile快捷键cmd+ i,再选对应的检测。

寻找耗电来源

  • 连接真机->command+R运行项目->command+7打开->点击Energy impact查看电量情况,如图
  • Average Energy Impact表示App的总体平均消耗电量评级。分为三部分,理想的状态时low、high状态。
    • low的时候指向绿色区域
    • high的时候指向黄色区域
    • very high指向红色区域
  • Average Component Utilizationn帮助我们快速了解各耗电部分的占比。当Average Energy Impact为very high的状态时要找出下面哪部分占比大,进行相应的优化
    • Overhead表示不是由App引起的耗电,可以忽略
    • Network表示网络能耗占总能耗的百分比
    • CPU表示CPU能耗占总能耗的百分比
    • GPU表示GPU能耗占总能耗的百分比
    • Location表示定位能耗占总能耗的百分比
  • Component Utilization每根柱子代表每秒钟的耗电情况,每根柱子由不同颜色组成,不同颜色代表的意思参考Average Component Utilizationn
  • 通过以上方法找出耗电占比大的某项(Network、CPU、GPU、Location),通过Profiler找出耗电详情,具体方法如下
    • CPU点击Time Profiler按钮进入
    • 网络点击Network Profiler按钮进入
    • 定位点击Location Profiler按钮进入
    • GPU点击Product->Profile->Core Animation按钮进入

Time Profiler

  • 这里拿Time Profiler进行举例,点击Time Profiler进入后,左下角点击Call Tree进行过滤
    • Separate by Thread按线程进行分析,便于找到消耗资源大的线程。
    • Invert Call Tree反向输出调用树,把调用层级反向输出,更容易找到最耗时的操作。
    • Hide System Libraries隐藏系统库文件,只显示自己的代码调用。
    • Top Functions找到最耗时的函数或方法。
  • 在调用树的表格中,按权重(weight)展开,要干掉的就是权重大的,耗时间的。
  • 接着展开Main Thread(其他线程的耗时,相比主线程的,可忽略 ), 按住Option键,点击 Main Thread左边的小三角,可以一下子展开很多。
  • 找到最耗时的方法点击两次进入详情,这个时候可以优化了

Energy Log

  • 方式一,适用于测试人员,测试时不需要把手机连接到Xcode,测试完成后在连接到Xcode查看数据
    • iPhone->设置->开发者->Loggin->Start Recording
    • 然后打开要测试的App随意点击,Stop Recording
    • Xcode->Open Developer Tool->Instruments->Energy Log
      • 选择要测试的设备和App
      • 左上角File->Import Logged Data From Device
  • 方式二,适用于开发人员,测试时需要把手机连接到Xcode
    • 双击打开EnergyLog直接点击红色圆点运行程序
    • App启动后,在里面进行各种操作
    • 点击黑色方块停止检测,这个时候可以看到数据,如果正在检测是看不到数据的
  • 使用说明
    • 可以看到CPU活动比例,网络活动比例,亮度状态,手机睡眠状态,手机连接蓝牙状态,手机连接wifi状态,手机GPS状态这次都是手机耗电的构成部分。
    • level值为0–20,值越大表示越耗电。1/20表示运行该app电池生命会有20个小时;20/20表示运行该app电池电量仅有1小时生命
    • WWDC2018里介绍了用Energy Log来查CPU耗电,当前台三分钟或后台一分钟CPU线程连续占用80%以上就判定为耗电,同时记录耗电线程堆栈供分析

sysdiagnose

电量单位

  • 电压
    • 电压以mV计,通过硬件测得,是计算其他数据的基础
    • iPhone工作时,电压几乎一直恒定在4V左右,测试过程中出现过的最高电压是4.3V。
    • 当电池剩余电量越少时,电压值会变得越小,但波动不大。
    • 电压过小时,可能会引起手机直接关机,这也是为什么有时还有20%电量,但手机却开不了机了。
    • 新的电池,电压波动会小一些,越是老化,电压波动可能越大。
    • 电压这个值能用来判断当前电池的健康度。
  • 电流
    • 电流以mA计,直接通过硬件测得,是计算其他数据的基础,
    • iPhone工作时,电流一般在1mA到700mA之间。超过500mA电池很容易发热。
  • 温度
    • 通过硬件接口获得,可以作为一个参考值
    • 测试过程中出现的最高温度是37度,能明显感觉到发热了。
  • mAh(毫安时):
    • 电池容量的计量单位,实际就是电池中可以释放为外部使用的电子的总数,需要乘上当前的电压(6P是4v)才是真正的能量。
  • 公式
    • W=UIt=Pt,P=UI
      • W表示电工单位为J,U表示电压,I表示电流,t表示时间单位为s或h,P表示功率单位为W
      • 电功=电压×电流×时间=功率×时间
    • P=UI=W/t
      • 功率=电压×电流=电功÷时间

日志

  • 日志记录对电源使用没有影响,但是如果启用数天而不进行同步,它确实会累积磁盘空间。 为了避免这种磁盘使用情况的积累,一旦提供了所需的诊断信息,请关闭日志记录。
  • 电池日志文件以powerlog_开头和以.PLSQL.PLSQL.gz结尾
  • 启用日志记录
    • 苹果开发者网站找到Battery Lift下载profile(需要登录开发者账号)
    • 下载完成后通过隔空投送发到测试手机上安装。
    • 不要重启手机,等待10到30分钟,手机连上电脑,通过 iTunes 同步到电脑上。
  • 日志位置
    • iOS: 设置 > 隐私 > 分析 > 分析数据 > (定位sysdiagnose文件,使用隔空投送发送到macOS上面).
    • macOS: ~/Library/Logs/CrashReporter/MobileDevice/[Your_Device_Name]/
    • Windows 10:
      1
      2
      C:\Users\USERID\AppData\Local\Packages\AppleInc.iTunes_devicename\LocalCache\Roaming\Apple
      Computer\Logs\CrashReporter\MobileDevice\[Your_Device_Name]/DiagnosticLogs/
  • 禁用日志记录
    • Launch Settings.
    • Tap General > Profiles.
    • Tap appropriate profile.
    • Tap Remove and enter passcode if asked.
    • Restart device.
  • 找到powerlog_2020-01-06-2020-01-09_0AF1E160.PLSQL,使用SQLPro for SQLiteNavicat Premius打开,发现里面有几百张表,所有的电量数据都在里面,主要的几张表的意思如下
    • PLBatteryAgent_EventBackward_Battery:整台机器的电量数据,包含电流、电压、温度等,每20秒左右一条数据
    • PLBatteryAgent_EventBackward_BatteryUI:电量百分比数据,大于每300秒一条数据
    • PLIOReportAgent_EventBackward_EneryModel: 整机的详细电量数据。包含 CPU\GPU\DRAM\ISP 等关键信息。每半小时到一小时一条数据。
    • PLAccountingOperator_EventNone_Nodes:App结点信息,每个APP对应唯一的结点号。用来确定手机内具体哪个 App。
    • PLApplicationAgent_EventForward_Application:App运行信息,记录每个App在哪个时间段以什么状态运行
    • PLAppTimeService_Aggregate_AppRunTime:APP的运行时长统计,每个运行过的APP,一小时一条数据
    • PLAccountingOperator_Aggregate_RootNodeEnergy: APP的电量详细数据,记录每个APP 的CPU\GPU\DRAM\ISP 等的耗电信息。一小时更新一次数据。
      • RootNodeID:6 isp
      • RootNodeID:52 apsocbase
      • RootNodeID:10 display
      • RootNodeID:11 wifi data
      • RootNodeID:8 GPU
      • RootNodeID:4 venc
      • RootNodeID:2 cpu
      • RootNodeID:7 restofsoc

测试方式

  • 先去PLAccountingOperator_EventNone_Nodes通过BundleID找到我们要测试的App的I我们要测试微信,其实可以发现,表中对应的Name字段就是BundleID,微信对应的ID为10521。
  • 得到每个App的唯一标识后,我们就可以去PLAccountingOperator_Aggregate_RootNodeEnergy表里看电量消耗数据了:
    1
    SELECT *,DATETIME(timestamp,'unixepoch','localtime') from PLAccountingOperator_Aggregate_RootNodeEnergy WHERE NodeID=10521 ORDER BY timestamp DESC;
  • 表中的Energy就是对应消耗的电量了,这里的单位在iOS9是mAh,iOS9及以上应该是 1/1000 mAh。比方测试2020-01-08 17:00:00这个时间耗电量,可以执行SQL语句计算出来。这个耗电量为3182 / 1000 = 3.182 mAh
    1
    SELECT SUM(Energy) from PLAccountingOperator_Aggregate_RootNodeEnergy WHERE NodeID=10521 AND timestamp=1578474000 ORDER BY timestamp DESC;
  • 如果想知道运行这个App这段时间内的温度,可以PLBatteryAgent_EventBackward_Battery 表中获取,但是因为该表的数据是整个机器的,所以我们需要根据对应的时间节点来观察数据: