应用CPU耗电计算 = APP运行中的每个CPU消耗时间和电量的总和,具体算法如下
其中一些应用没有电量消耗问题或者电量不准确,重点看下 time_in_state 节点有没有生成和power_profile.xml是否被正确配置
@Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
// Aggregate total time spent on each cluster.
long totalTime = 0;
// 获取 CPU 群簇数量,即 cpu.clusters.cores 的size
final int numClusters = mProfile.getNumCpuClusters();
// 遍历每个 CPU 群簇数量 cpu.clusters.cores
for (int cluster = 0; cluster < numClusters; cluster++) {
// 获取每个 CPU 群簇的CPU档位size,例如 cpu.speeds.cluster0 的档位数量
final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < speedsForCluster; speed++) {
// 累增每个CPU档位的消耗时间
totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
}
}
totalTime = Math.max(totalTime, 1);
double cpuPowerMaMs = 0;
// 遍历每个 CPU 群簇数量 cpu.clusters.cores
for (int cluster = 0; cluster < numClusters; cluster++) {
// 获取每个 CPU 群簇的CPU档位size,例如 cpu.speeds.cluster0 的档位数量
final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < speedsForCluster; speed++) {
// ratio = 每个档位的CPU消耗时间 / 总运行时间
final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
totalTime;
// cpuSpeedStepPower 计算每个CPU频率档位消耗的电流
final double cpuSpeedStepPower = ratio * app.cpuTimeMs *
mProfile.getAveragePowerForCpu(cluster, speed);
if (DEBUG && ratio != 0) {
Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+ speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
+ BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
}
// 应用CPU消耗总电流等于应用在每个CPU单位消耗的电量之和
cpuPowerMaMs += cpuSpeedStepPower;
}
}
app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
+ BatteryStatsHelper.makemAh(app.cpuPowerMah));
}
这里主要是 LongSamplingCounter 进行CPU时间计算,这里主要是使用 time_in_state 的节点数据
LongSamplingCounter mCpuClusterSpeed;
@Override
public long getTimeAtCpuSpeed(int cluster, int step, int which) {
if (mCpuClusterSpeed != null) {
if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster];
if (cpuSpeeds != null) {
if (step >= 0 && step < cpuSpeeds.length) {
final LongSamplingCounter c = cpuSpeeds[step];
if (c != null) {
return c.getCountLocked(which);
}
}
}
}
}
return 0;
}
统计的对象是 time_in_state 节点的数据
adb shell "cat /sys/devices/system/cpu/cpu5/cpufreq/stats/time_in_state"
1500000 20000
1429000 495
1367000 549
1314000 528
1261000 578
1208000 12754
1155000 92
1102000 54
1050000 642
948000 186
846000 261
745000 142
643000 168
542000 182
460000 171
400000 31079
/**
* Reads CPU time of a specific core spent at various frequencies and provides a delta from the
* last call to {@link #readDelta}. Each line in the proc file has the format:
*
* freq time
*
* where time is measured in jiffies.
*/
public class KernelCpuSpeedReader {
private static final String TAG = "KernelCpuSpeedReader";
private final String mProcFile;
private final long[] mLastSpeedTimes;
private final long[] mDeltaSpeedTimes;
// How long a CPU jiffy is in milliseconds.
private final long mJiffyMillis;
/**
* @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read.
*/
public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
cpuNumber);
mLastSpeedTimes = new long[numSpeedSteps];
mDeltaSpeedTimes = new long[numSpeedSteps];
long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK);
mJiffyMillis = 1000/jiffyHz;
}
配置下 CONFIG_CPU_FREQ_STAT=y 宏即可
https://blog.csdn.net/su749520/article/details/83385322
/**
* Read and distribute CPU usage across apps. If their are partial wakelocks being held
* and we are on battery with screen off, we give more of the cpu time to those apps holding
* wakelocks. If the screen is on, we just assign the actual cpu time an app used.
*/
public void updateCpuTimeLocked() {
if (mPowerProfile == null) {
return;
}
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "!Cpu updating!");
}
// Holding a wakelock costs more than just using the cpu.
// Currently, we assign only half the cpu time to an app that is running but
// not holding a wakelock. The apps holding wakelocks get the rest of the blame.
// If no app is holding a wakelock, then the distribution is normal.
final int wakelockWeight = 50;
// Read the time spent for each cluster at various cpu frequencies.
final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
}
当 KernelUidCpuTimeReader 中的文件节点发生变化的时候刷新,对UID下的CPU运行时间
// Read the CPU data for each UID. This will internally generate a snapshot so next time
// we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise
// we just ignore the data.
final long startTimeMs = mClocks.elapsedRealtime();
mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
new KernelUidCpuTimeReader.Callback() {
...
// Add the cpu speeds to this UID. These are used as a ratio
// for computing the power this UID used.
final int numClusters = mPowerProfile.getNumCpuClusters();
if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length !=
numClusters) {
u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
}
for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(
cluster);
if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster !=
u.mCpuClusterSpeed[cluster].length) {
u.mCpuClusterSpeed[cluster] =
new LongSamplingCounter[speedsInCluster];
}
final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
for (int speed = 0; speed < clusterSpeeds[cluster].length; speed++) {
if (cpuSpeeds[speed] == null) {
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
}
// 累加数据,最终会存了BatteryState.bin文件中
cpuSpeeds[speed].addCountLocked(clusterSpeeds[cluster][speed]);
}
}
具体可以查看
https://blog.csdn.net/su749520/article/details/83340832
/**
* 获取cpu.active.cluster[cluster].[step] 对应下标的CPU耗电信息
* 例如cpu.active.cluster0下第0下标的电流为100 mA
*/
public double getAveragePowerForCpu(int cluster, int step) {
if (cluster >= 0 && cluster < mCpuClusters.length) {
return getAveragePower(mCpuClusters[cluster].powerKey, step);
}
return 0;
}
上述数据,需要手机厂商根据整机进行测试,并填入power_profile文件中
- 0
- 200
- 300
- 66
- 0.5
- 4.83
- 166
- 20
- 39
- 145
- 109
- 659
- 50
- 200
- 32
2
1
- 0
- 0
- 0
- 0
4
4
900000
979000
1085000
1218000
1351000
1484000
1617000
1750000
1779000
1809000
1838000
1868000
1897000
1927000
1961000
2001000
400000
460000
542000
643000
745000
846000
948000
1050000
1102000
1155000
1208000
1261000
1314000
1367000
1429000
1500000
100
110
123
134
142
153
163
171
186
190
201
213
223
236
248
256
100
114
125
134
140
147
152
158
164
169
176
181
185
190
195
200
- 6
22.7
- 3000
- 0
- 0
- 0
- 0
.0002
.002
.02
.2
2