From a3cc16d9c9ca8c8d13290fcfe14db57220a0dc21 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Tue, 9 Jul 2024 19:35:56 +0200 Subject: [PATCH 1/3] feat: improve output of metadata --- .../sensors/macos/powermetrics/MacOSPowermetricsSensor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java index 191be714..1c4980f7 100644 --- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java +++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; From 15e1b360ef7190a31e2620a09944ffd3c50f0a90 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Tue, 9 Jul 2024 20:18:09 +0200 Subject: [PATCH 2/3] refactor: abstract away value store --- .../DescriptiveStatisticsMeasureStore.java | 46 +++++++++++++++++++ .../power/measure/MeasureStore.java | 15 ++++++ .../power/measure/OngoingPowerMeasure.java | 27 +++++------ .../powermetrics/MacOSPowermetricsSensor.java | 1 - 4 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java create mode 100644 measure/src/main/java/net/laprun/sustainability/power/measure/MeasureStore.java diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java b/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java new file mode 100644 index 00000000..a416dfec --- /dev/null +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java @@ -0,0 +1,46 @@ +package net.laprun.sustainability.power.measure; + +import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; + +class DescriptiveStatisticsMeasureStore implements MeasureStore { + private final DescriptiveStatistics[] measures; + private final DescriptiveStatistics total; + + public DescriptiveStatisticsMeasureStore(int componentsNumber, int initialWindow) { + total = new DescriptiveStatistics(initialWindow); + this.measures = new DescriptiveStatistics[componentsNumber]; + for (int i = 0; i < measures.length; i++) { + measures[i] = new DescriptiveStatistics(initialWindow); + } + } + + @Override + public void recordComponentValue(int component, double value) { + measures[component].addValue(value); + } + + @Override + public void recordTotal(double value) { + total.addValue(value); + } + + @Override + public double getMeasuredTotal() { + return total.getSum(); + } + + @Override + public double getComponentStandardDeviation(int component) { + return measures[component].getStandardDeviation(); + } + + @Override + public double getTotalStandardDeviation() { + return total.getStandardDeviation(); + } + + @Override + public double[] getComponentRawValues(int component) { + return measures[component].getValues(); + } +} diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/MeasureStore.java b/measure/src/main/java/net/laprun/sustainability/power/measure/MeasureStore.java new file mode 100644 index 00000000..c878b434 --- /dev/null +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/MeasureStore.java @@ -0,0 +1,15 @@ +package net.laprun.sustainability.power.measure; + +interface MeasureStore { + void recordComponentValue(int component, double value); + + void recordTotal(double value); + + double getMeasuredTotal(); + + double getComponentStandardDeviation(int component); + + double getTotalStandardDeviation(); + + double[] getComponentRawValues(int component); +} diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java b/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java index f3e21db4..fef448fb 100644 --- a/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java @@ -4,17 +4,15 @@ import java.util.HashSet; import java.util.Set; -import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; - import net.laprun.sustainability.power.SensorMetadata; public class OngoingPowerMeasure implements PowerMeasure { private final SensorMetadata sensorMetadata; - private final DescriptiveStatistics[] measures; - private final DescriptiveStatistics total; + private final MeasureStore measures; private final long startedAt; private final double[] averages; private final Set nonZeroComponents; + private final int[] totalComponents; private double minTotal = Double.MAX_VALUE; private double maxTotal; private int samples; @@ -26,13 +24,10 @@ public OngoingPowerMeasure(SensorMetadata sensorMetadata, Duration duration, Dur averages = new double[numComponents]; final var initialWindow = (int) (duration.toMillis() / frequency.toMillis()); - total = new DescriptiveStatistics(initialWindow); - this.measures = new DescriptiveStatistics[numComponents]; - for (int i = 0; i < measures.length; i++) { - measures[i] = new DescriptiveStatistics(initialWindow); - } + measures = new DescriptiveStatisticsMeasureStore(numComponents, initialWindow); nonZeroComponents = new HashSet<>(numComponents); + totalComponents = sensorMetadata.totalComponents(); } @Override @@ -54,14 +49,14 @@ public void recordMeasure(double[] components) { if (componentValue != 0) { nonZeroComponents.add(component); } - measures[component].addValue(componentValue); + measures.recordComponentValue(component, componentValue); averages[component] = averages[component] == 0 ? componentValue : (previousSize * averages[component] + componentValue) / samples; } // record min / max totals - final var recordedTotal = PowerMeasure.sumOfSelectedComponents(components, metadata().totalComponents()); - total.addValue(recordedTotal); + final var recordedTotal = PowerMeasure.sumOfSelectedComponents(components, totalComponents); + measures.recordTotal(recordedTotal); if (recordedTotal < minTotal) { minTotal = recordedTotal; } @@ -72,7 +67,7 @@ public void recordMeasure(double[] components) { @Override public double total() { - return total.getSum(); + return measures.getMeasuredTotal(); } public Duration duration() { @@ -99,13 +94,13 @@ public StdDev standardDeviations() { final var stdDevs = new double[cardinality]; nonZeroComponents.stream() .parallel() - .forEach(component -> stdDevs[component] = measures[component].getStandardDeviation()); + .forEach(component -> stdDevs[component] = measures.getComponentStandardDeviation(component)); - return new StdDev(total.getStandardDeviation(), stdDevs); + return new StdDev(measures.getTotalStandardDeviation(), stdDevs); } @Override public double[] getMeasuresFor(int component) { - return measures[component].getValues(); + return measures.getComponentRawValues(component); } } diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java index 1c4980f7..191be714 100644 --- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java +++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; From bc15b2b5b26fef565a3a7b704ae19cdd8d9c8ea3 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 10 Jul 2024 22:13:01 +0200 Subject: [PATCH 3/3] feat: added HdrHistogram-backed MeasureStore --- measure/pom.xml | 5 ++ .../DescriptiveStatisticsMeasureStore.java | 26 ++++--- .../measure/HdrHistogramMeasureStore.java | 69 +++++++++++++++++++ 3 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 measure/src/main/java/net/laprun/sustainability/power/measure/HdrHistogramMeasureStore.java diff --git a/measure/pom.xml b/measure/pom.xml index dff0b6ea..446166ed 100644 --- a/measure/pom.xml +++ b/measure/pom.xml @@ -23,6 +23,11 @@ commons-math3 3.6.1 + + org.hdrhistogram + HdrHistogram + 2.2.2 + diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java b/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java index a416dfec..0e08df85 100644 --- a/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/DescriptiveStatisticsMeasureStore.java @@ -3,12 +3,12 @@ import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; class DescriptiveStatisticsMeasureStore implements MeasureStore { + private final int totalIndex; private final DescriptiveStatistics[] measures; - private final DescriptiveStatistics total; public DescriptiveStatisticsMeasureStore(int componentsNumber, int initialWindow) { - total = new DescriptiveStatistics(initialWindow); - this.measures = new DescriptiveStatistics[componentsNumber]; + this.measures = new DescriptiveStatistics[componentsNumber + 1]; + totalIndex = componentsNumber; for (int i = 0; i < measures.length; i++) { measures[i] = new DescriptiveStatistics(initialWindow); } @@ -16,31 +16,39 @@ public DescriptiveStatisticsMeasureStore(int componentsNumber, int initialWindow @Override public void recordComponentValue(int component, double value) { - measures[component].addValue(value); + getMeasure(component).addValue(value); + } + + private DescriptiveStatistics getMeasure(int component) { + return measures[component]; + } + + private DescriptiveStatistics getTotalMeasure() { + return getMeasure(totalIndex); } @Override public void recordTotal(double value) { - total.addValue(value); + getTotalMeasure().addValue(value); } @Override public double getMeasuredTotal() { - return total.getSum(); + return getTotalMeasure().getSum(); } @Override public double getComponentStandardDeviation(int component) { - return measures[component].getStandardDeviation(); + return getMeasure(component).getStandardDeviation(); } @Override public double getTotalStandardDeviation() { - return total.getStandardDeviation(); + return getTotalMeasure().getStandardDeviation(); } @Override public double[] getComponentRawValues(int component) { - return measures[component].getValues(); + return getMeasure(component).getValues(); } } diff --git a/measure/src/main/java/net/laprun/sustainability/power/measure/HdrHistogramMeasureStore.java b/measure/src/main/java/net/laprun/sustainability/power/measure/HdrHistogramMeasureStore.java new file mode 100644 index 00000000..1d5c5663 --- /dev/null +++ b/measure/src/main/java/net/laprun/sustainability/power/measure/HdrHistogramMeasureStore.java @@ -0,0 +1,69 @@ +package net.laprun.sustainability.power.measure; + +import org.HdrHistogram.HistogramIterationValue; +import org.HdrHistogram.IntCountsHistogram; + +public class HdrHistogramMeasureStore implements MeasureStore { + private static final int HIGHEST_TRACKABLE_VALUE = 1_000_000; + private static final int NUMBER_OF_SIGNIFICANT_VALUE_DIGITS = 4; + private static final int CONVERSION_FACTOR = 1000; + private final IntCountsHistogram[] measures; + private final int totalIndex; + private double accumulatedTotal; + + public HdrHistogramMeasureStore(int componentsNumber, int initialWindow) { + totalIndex = componentsNumber; + measures = new IntCountsHistogram[componentsNumber + 1]; + for (int i = 0; i < measures.length; i++) { + measures[i] = new IntCountsHistogram(HIGHEST_TRACKABLE_VALUE, + NUMBER_OF_SIGNIFICANT_VALUE_DIGITS); + } + } + + private IntCountsHistogram getMeasure(int component) { + return measures[component]; + } + + private IntCountsHistogram getTotalMeasure() { + return getMeasure(totalIndex); + } + + @Override + public void recordComponentValue(int component, double value) { + getMeasure(component).recordValue((long) (CONVERSION_FACTOR * value)); + } + + @Override + public void recordTotal(double value) { + getTotalMeasure().recordValue((long) (CONVERSION_FACTOR * value)); + accumulatedTotal += value; + } + + @Override + public double getMeasuredTotal() { + return accumulatedTotal; + } + + @Override + public double getComponentStandardDeviation(int component) { + return getMeasure(component).getStdDeviation() / CONVERSION_FACTOR; + } + + @Override + public double getTotalStandardDeviation() { + // not unbiased so tests will fail + return getTotalMeasure().getStdDeviation(); + } + + @Override + public double[] getComponentRawValues(int component) { + final var measure = getMeasure(component); + final var totalCount = measure.getTotalCount(); + final var result = new double[(int) totalCount]; + int index = 0; + for (HistogramIterationValue value : measure.recordedValues()) { + result[index++] = (double) value.getValueIteratedTo() / CONVERSION_FACTOR; + } + return result; + } +}