diff options
| author | 2023-10-19 13:56:03 -0700 | |
|---|---|---|
| committer | 2023-10-25 17:08:01 -0700 | |
| commit | fcaf40e8f3b35a6966978f48ba3c73f97075cbb6 (patch) | |
| tree | 10cb4705394544562a375fc36df90bef6e518368 | |
| parent | 2b29041f04076a3ba38a2980eb9f47e4a9f4f26e (diff) | |
Add power estimation (energy consumer based) to aggregated stats
Bug: 302013436
Test: atest PowerStatsTests
Change-Id: I19917ce861937da19268d1021f55e220d5bed64a
5 files changed, 232 insertions, 18 deletions
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java index f365a5439219..5fd8ddfbf240 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java @@ -44,6 +44,7 @@ abstract class AggregatedPowerStatsProcessor { private static final String TAG = "AggregatedPowerStatsProcessor"; private static final int INDEX_DOES_NOT_EXIST = -1; + private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0; abstract void finish(PowerComponentAggregatedPowerStats stats); @@ -340,4 +341,8 @@ abstract class AggregatedPowerStatsProcessor { }); return combinations.toArray(new int[combinations.size()][0]); } + + public static double uCtoMah(long chargeUC) { + return chargeUC * MILLIAMPHOUR_PER_MICROCOULOMB; + } } diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java index 8dc232fbfc81..f40eef2ed820 100644 --- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java @@ -17,6 +17,7 @@ package com.android.server.power.stats; import android.os.BatteryStats; +import android.util.ArraySet; import android.util.Log; import com.android.internal.os.CpuScalingPolicies; @@ -24,6 +25,7 @@ import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -31,7 +33,9 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private static final String TAG = "CpuAggregatedPowerStatsProcessor"; private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1); + private static final int UNKNOWN = -1; + private final CpuScalingPolicies mCpuScalingPolicies; // Number of CPU core clusters private final int mCpuClusterCount; // Total number of CPU scaling steps across all clusters @@ -44,6 +48,17 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private final double[] mPowerMultipliersByCluster; // Average power consumed by each scaling step when running code (per power_profile.xml) private final double[] mPowerMultipliersByScalingStep; + // A map used to combine energy consumers into a smaller set, in case power brackets + // are defined in a way that does not allow an unambiguous mapping of energy consumers to + // brackets + private int[] mEnergyConsumerToCombinedEnergyConsumerMap; + // A map of combined energy consumers to the corresponding collections of power brackets. + // For example, if there are two CPU_CLUSTER rails and each maps to three brackets, + // this map will look like this: + // 0 : [0, 1, 2] + // 1 : [3, 4, 5] + private int[][] mCombinedEnergyConsumerToPowerBracketMap; + // Cached reference to a PowerStats descriptor. Almost never changes in practice, // helping to avoid reparsing the descriptor for every PowerStats span. private PowerStats.Descriptor mLastUsedDescriptor; @@ -60,6 +75,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) { + mCpuScalingPolicies = scalingPolicies; mCpuScalingStepCount = scalingPolicies.getScalingStepCount(); mScalingStepToCluster = new int[mCpuScalingStepCount]; mPowerMultipliersByScalingStep = new double[mCpuScalingStepCount]; @@ -111,6 +127,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces public long[] timeByScalingStep; public double[] powerByCluster; public double[] powerByScalingStep; + public long[] powerByEnergyConsumer; } /** @@ -132,6 +149,9 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces if (mPlan == null) { mPlan = new PowerEstimationPlan(stats.getConfig()); + if (mStatsLayout.getCpuClusterEnergyConsumerCount() != 0) { + initEnergyConsumerToPowerBracketMaps(); + } } Intermediates intermediates = new Intermediates(); @@ -171,6 +191,96 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces mPlan.resetIntermediates(); } + /* + * Populate data structures (two maps) needed to use power rail data, aka energy consumers, + * to attribute power usage to apps. + * + * At this point, the algorithm covers only the most basic cases: + * - Each cluster is mapped to unique power brackets (possibly multiple for each cluster): + * CL_0: [bracket0, bracket1] + * CL_1: [bracket3] + * In this case, the consumed energy is distributed to the corresponding brackets + * proportionally. + * - Brackets span multiple clusters: + * CL_0: [bracket0, bracket1] + * CL_1: [bracket1, bracket2] + * CL_2: [bracket3, bracket4] + * In this case, we combine energy consumers into groups unambiguously mapped to + * brackets. In the above example, consumed energy for CL_0 and CL_1 will be combined + * because they both map to the same power bracket (bracket1): + * (CL_0+CL_1): [bracket0, bracket1, bracket2] + * CL_2: [bracket3, bracket4] + */ + private void initEnergyConsumerToPowerBracketMaps() { + int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); + int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); + + mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount]; + mCombinedEnergyConsumerToPowerBracketMap = new int[energyConsumerCount][]; + + int[] policies = mCpuScalingPolicies.getPolicies(); + if (energyConsumerCount == policies.length) { + int[] scalingStepToPowerBracketMap = mStatsLayout.getScalingStepToPowerBracketMap(); + ArraySet<Integer>[] clusterToBrackets = new ArraySet[policies.length]; + int step = 0; + for (int cluster = 0; cluster < policies.length; cluster++) { + int[] freqs = mCpuScalingPolicies.getFrequencies(policies[cluster]); + clusterToBrackets[cluster] = new ArraySet<>(freqs.length); + for (int j = 0; j < freqs.length; j++) { + clusterToBrackets[cluster].add(scalingStepToPowerBracketMap[step++]); + } + } + + ArraySet<Integer>[] combinedEnergyConsumers = new ArraySet[policies.length]; + int combinedEnergyConsumersCount = 0; + + for (int cluster = 0; cluster < clusterToBrackets.length; cluster++) { + int combineWith = UNKNOWN; + for (int i = 0; i < combinedEnergyConsumersCount; i++) { + if (containsAny(combinedEnergyConsumers[i], clusterToBrackets[cluster])) { + combineWith = i; + break; + } + } + if (combineWith != UNKNOWN) { + mEnergyConsumerToCombinedEnergyConsumerMap[cluster] = combineWith; + combinedEnergyConsumers[combineWith].addAll(clusterToBrackets[cluster]); + } else { + mEnergyConsumerToCombinedEnergyConsumerMap[cluster] = + combinedEnergyConsumersCount; + combinedEnergyConsumers[combinedEnergyConsumersCount++] = + clusterToBrackets[cluster]; + } + } + + for (int i = combinedEnergyConsumers.length - 1; i >= 0; i--) { + mCombinedEnergyConsumerToPowerBracketMap[i] = + new int[combinedEnergyConsumers[i].size()]; + for (int j = combinedEnergyConsumers[i].size() - 1; j >= 0; j--) { + mCombinedEnergyConsumerToPowerBracketMap[i][j] = + combinedEnergyConsumers[i].valueAt(j); + } + } + } else { + // All CPU cluster energy consumers are combined into one, which is + // distributed proportionally to all power brackets. + int[] map = new int[powerBracketCount]; + for (int i = 0; i < map.length; i++) { + map[i] = i; + } + mCombinedEnergyConsumerToPowerBracketMap[0] = map; + } + } + + private static boolean containsAny(ArraySet<Integer> set1, ArraySet<Integer> set2) { + for (int i = 0; i < set2.size(); i++) { + if (set1.contains(set2.valueAt(i))) { + return true; + } + } + return false; + } + private void computeTotals(PowerComponentAggregatedPowerStats stats, Intermediates intermediates) { intermediates.timeByScalingStep = new long[mCpuScalingStepCount]; @@ -241,6 +351,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount(); int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap(); + int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations; for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) { DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse); @@ -251,7 +362,6 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces deviceStatsIntermediates.powerByBracket = new double[powerBracketCount]; stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues); - double power = 0; for (int step = 0; step < cpuScalingStepCount; step++) { if (intermediates.timeByScalingStep[step] == 0) { continue; @@ -260,27 +370,77 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step); double stepPower = intermediates.powerByScalingStep[step] * timeInStep / intermediates.timeByScalingStep[step]; - power += stepPower; int bracket = scalingStepToBracketMap[step]; deviceStatsIntermediates.timeByBracket[bracket] += timeInStep; deviceStatsIntermediates.powerByBracket[bracket] += stepPower; } + + if (energyConsumerCount != 0) { + adjustEstimatesUsingEnergyConsumers(intermediates, deviceStatsIntermediates); + } + + double power = 0; + for (int i = deviceStatsIntermediates.powerByBracket.length - 1; i >= 0; i--) { + power += deviceStatsIntermediates.powerByBracket[i]; + } deviceStatsIntermediates.power = power; mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power); stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray); } } + private void adjustEstimatesUsingEnergyConsumers( + Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) { + int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); + if (energyConsumerCount == 0) { + return; + } + + if (intermediates.powerByEnergyConsumer == null) { + intermediates.powerByEnergyConsumer = new long[energyConsumerCount]; + } else { + Arrays.fill(intermediates.powerByEnergyConsumer, 0); + } + for (int i = 0; i < energyConsumerCount; i++) { + intermediates.powerByEnergyConsumer[mEnergyConsumerToCombinedEnergyConsumerMap[i]] += + mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i); + } + + for (int combinedConsumer = mCombinedEnergyConsumerToPowerBracketMap.length - 1; + combinedConsumer >= 0; combinedConsumer--) { + int[] combinedEnergyConsumerToPowerBracketMap = + mCombinedEnergyConsumerToPowerBracketMap[combinedConsumer]; + if (combinedEnergyConsumerToPowerBracketMap == null) { + continue; + } + + double consumedEnergy = uCtoMah(intermediates.powerByEnergyConsumer[combinedConsumer]); + + double totalModeledPower = 0; + for (int bracket : combinedEnergyConsumerToPowerBracketMap) { + totalModeledPower += deviceStatsIntermediates.powerByBracket[bracket]; + } + if (totalModeledPower == 0) { + continue; + } + + for (int bracket : combinedEnergyConsumerToPowerBracketMap) { + deviceStatsIntermediates.powerByBracket[bracket] = + consumedEnergy * deviceStatsIntermediates.powerByBracket[bracket] + / totalModeledPower; + } + } + } + private void combineDeviceStateEstimates() { for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) { CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i); - DeviceStatsIntermediates cdseIntermediates = - new DeviceStatsIntermediates(); + DeviceStatsIntermediates cdseIntermediates = new DeviceStatsIntermediates(); + cdse.intermediates = cdseIntermediates; int bracketCount = mStatsLayout.getCpuPowerBracketCount(); cdseIntermediates.timeByBracket = new long[bracketCount]; cdseIntermediates.powerByBracket = new double[bracketCount]; - cdse.intermediates = cdseIntermediates; List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations; for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) { DeviceStateEstimation dse = deviceStateEstimations.get(j); @@ -350,7 +510,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces sb.append(mStatsLayout.getTimeByCluster(stats, cluster)); } sb.append("] uptime: ").append(mStatsLayout.getUptime(stats)); - int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); + int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount(); if (energyConsumerCount > 0) { sb.append(" energy: ["); for (int i = 0; i < energyConsumerCount; i++) { diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index 7bb7ee10670c..a388932cb708 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java @@ -120,6 +120,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private int mUidPowerBracketsPosition; private int mUidPowerBracketCount; + private int[][] mEnergyConsumerToPowerBucketMaps; private int mUidPowerEstimatePosition; private int[] mScalingStepToPowerBracketMap; @@ -221,7 +222,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { mDeviceStatsArrayLength += energyConsumerCount; } - public int getEnergyConsumerCount() { + public int getCpuClusterEnergyConsumerCount() { return mDeviceEnergyConsumerCount; } @@ -492,9 +493,8 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { + mCpuScalingPolicies.getPolicies().length + ") does not match the number of energy consumers (" + mCpuEnergyConsumerIds.length + "). " - + " Please specify power bracket assignment explicitly in" - + " power_profile.xml"); - return initPowerBracketsByCluster(1); + + " Using default power bucket assignment."); + return initDefaultPowerBrackets(mDefaultCpuPowerBrackets); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java index a3b69ff4f684..79084cc1b04d 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java @@ -71,23 +71,23 @@ public class CpuAggregatedPowerStatsProcessorTest { .setCpuPowerBracket(0, 1, 1) .setCpuPowerBracket(2, 0, 2); + private AggregatedPowerStatsConfig.PowerComponent mConfig; private CpuAggregatedPowerStatsProcessor mProcessor; private MockPowerComponentAggregatedPowerStats mStats; @Before public void setup() { - AggregatedPowerStatsConfig.PowerComponent powerComponent = - new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_CPU) - .trackDeviceStates(STATE_POWER, STATE_SCREEN) - .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE); + mConfig = new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_CPU) + .trackDeviceStates(STATE_POWER, STATE_SCREEN) + .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE); mProcessor = new CpuAggregatedPowerStatsProcessor( mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies()); - mStats = new MockPowerComponentAggregatedPowerStats(powerComponent); } @Test public void powerProfileModel() { + mStats = new MockPowerComponentAggregatedPowerStats(mConfig, false); mStats.setDeviceStats( states(POWER_STATE_BATTERY, SCREEN_STATE_ON), concat( @@ -127,6 +127,51 @@ public class CpuAggregatedPowerStatsProcessorTest { mStats.verifyPowerEstimates(); } + @Test + public void energyConsumerModel() { + mStats = new MockPowerComponentAggregatedPowerStats(mConfig, true); + mStats.setDeviceStats( + states(POWER_STATE_BATTERY, SCREEN_STATE_ON), + concat( + values(3500, 4500, 3000), // scaling steps + values(2000, 1000), // clusters + values(5000), // uptime + values(5_000_000L, 6_000_000L)), // energy, uC + 3.055555); + mStats.setDeviceStats( + states(POWER_STATE_OTHER, SCREEN_STATE_ON), + concat( + values(6000, 6500, 4000), + values(5000, 3000), + values(7000), + values(5_000_000L, 6_000_000L)), // same as above + 3.055555); // same as above - WAI + mStats.setDeviceStats( + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER), + concat( + values(9000, 10000, 7000), + values(8000, 6000), + values(20000), + values(8_000_000L, 18_000_000L)), + 7.222222); + mStats.setUidStats(24, + states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND), + values(400, 1500, 2000), 1.449078); + mStats.setUidStats(42, + states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND), + values(900, 1000, 1500), 1.161902); + mStats.setUidStats(42, + states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND), + values(600, 500, 300), 0.355406); + mStats.setUidStats(42, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED), + values(1500, 2000, 1000), 0.80773); + + mProcessor.finish(mStats); + + mStats.verifyPowerEstimates(); + } + private int[] states(int... states) { return states; } @@ -145,7 +190,7 @@ public class CpuAggregatedPowerStatsProcessorTest { return all.toArray(); } - private class MockPowerComponentAggregatedPowerStats extends + private static class MockPowerComponentAggregatedPowerStats extends PowerComponentAggregatedPowerStats { private final CpuPowerStatsCollector.StatsArrayLayout mStatsLayout; private final PowerStats.Descriptor mDescriptor; @@ -155,12 +200,16 @@ public class CpuAggregatedPowerStatsProcessorTest { private HashMap<String, Double> mExpectedDevicePower = new HashMap<>(); private HashMap<String, Double> mExpectedUidPower = new HashMap<>(); - MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) { + MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config, + boolean useEnergyConsumers) { super(config); mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout(); mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3); mStatsLayout.addDeviceSectionCpuTimeByCluster(2); mStatsLayout.addDeviceSectionUptime(); + if (useEnergyConsumers) { + mStatsLayout.addDeviceSectionEnergyConsumers(2); + } mStatsLayout.addDeviceSectionPowerEstimate(); mStatsLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0, 1, 2}); mStatsLayout.addUidSectionPowerEstimate(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java index f3362429a412..bc211df5f143 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java @@ -173,7 +173,7 @@ public class CpuPowerStatsCollectorTest { assertThat(layout.getCpuScalingStepCount()).isEqualTo(7); assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42); - assertThat(layout.getEnergyConsumerCount()).isEqualTo(2); + assertThat(layout.getCpuClusterEnergyConsumerCount()).isEqualTo(2); assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43); assertThat(layout.getUptime(deviceStats)).isEqualTo(44); |