summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitri Plotnikov <dplotnikov@google.com> 2023-10-19 13:56:03 -0700
committer Dmitri Plotnikov <dplotnikov@google.com> 2023-10-25 17:08:01 -0700
commitfcaf40e8f3b35a6966978f48ba3c73f97075cbb6 (patch)
tree10cb4705394544562a375fc36df90bef6e518368
parent2b29041f04076a3ba38a2980eb9f47e4a9f4f26e (diff)
Add power estimation (energy consumer based) to aggregated stats
Bug: 302013436 Test: atest PowerStatsTests Change-Id: I19917ce861937da19268d1021f55e220d5bed64a
-rw-r--r--services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java5
-rw-r--r--services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java172
-rw-r--r--services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java8
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java63
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java2
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);