Skip to content

Commit 3955a9a

Browse files
committed
refactor: move std deviation computation to separate analysis module
1 parent ce6d7b1 commit 3955a9a

File tree

8 files changed

+166
-64
lines changed

8 files changed

+166
-64
lines changed

analysis/pom.xml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>net.laprun.sustainability</groupId>
9+
<artifactId>power-server-parent</artifactId>
10+
<version>0.0.12-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>power-server-analysis</artifactId>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>net.laprun.sustainability</groupId>
18+
<artifactId>power-server-measure</artifactId>
19+
<version>${project.version}</version>
20+
</dependency>
21+
<dependency>
22+
<groupId>org.apache.commons</groupId>
23+
<artifactId>commons-math3</artifactId>
24+
<version>3.6.1</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.hdrhistogram</groupId>
28+
<artifactId>HdrHistogram</artifactId>
29+
<version>2.2.2</version>
30+
</dependency>
31+
</dependencies>
32+
33+
<build>
34+
<pluginManagement>
35+
<plugins>
36+
<plugin>
37+
<groupId>net.revelc.code.formatter</groupId>
38+
<artifactId>formatter-maven-plugin</artifactId>
39+
<dependencies>
40+
<dependency>
41+
<groupId>net.laprun.sustainability</groupId>
42+
<artifactId>build-tools</artifactId>
43+
<version>0.0.12-SNAPSHOT</version>
44+
</dependency>
45+
</dependencies>
46+
</plugin>
47+
</plugins>
48+
</pluginManagement>
49+
</build>
50+
51+
</project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package net.laprun.sustainability.power.analysis;
2+
3+
import org.apache.commons.math3.util.FastMath;
4+
5+
import net.laprun.sustainability.power.measure.PowerMeasure;
6+
7+
public enum Compute {
8+
;
9+
10+
public static double standardDeviation(PowerMeasure measure, int componentIndex) {
11+
return measure.getMeasuresFor(componentIndex).map(values -> {
12+
final var samples = measure.numberOfSamples();
13+
if (samples <= 1) {
14+
return 0.0;
15+
}
16+
final double mean = measure.averagesPerComponent()[componentIndex];
17+
double geometricDeviationTotal = 0.0;
18+
for (int index = 0; index < samples; index++) {
19+
double deviation = values[index] - mean;
20+
geometricDeviationTotal += (deviation * deviation);
21+
}
22+
return FastMath.sqrt(geometricDeviationTotal / (samples - 1));
23+
}).orElse(0.0);
24+
}
25+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import static org.junit.jupiter.api.Assertions.assertEquals;
2+
3+
import java.util.Map;
4+
import java.util.Random;
5+
import java.util.random.RandomGenerator;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
import net.laprun.sustainability.power.SensorMetadata;
10+
import net.laprun.sustainability.power.analysis.Compute;
11+
import net.laprun.sustainability.power.measure.OngoingPowerMeasure;
12+
13+
public class ComputeTest {
14+
private final static SensorMetadata metadata = new SensorMetadata(Map.of(), null, new int[0]) {
15+
16+
@Override
17+
public int componentCardinality() {
18+
return 3;
19+
}
20+
};
21+
22+
@Test
23+
void standardDeviationShouldWork() {
24+
final var random = Random.from(RandomGenerator.getDefault());
25+
final var m1c1 = random.nextDouble();
26+
final var m1c2 = random.nextDouble();
27+
final var m1c3 = 0.0;
28+
final var m2c1 = random.nextDouble();
29+
final var m2c2 = random.nextDouble();
30+
final var m2c3 = 0.0;
31+
final var m3c1 = random.nextDouble();
32+
final var m3c2 = random.nextDouble();
33+
final var m3c3 = 0.0;
34+
35+
final var measure = new OngoingPowerMeasure(metadata);
36+
37+
final var components = new double[metadata.componentCardinality()];
38+
components[0] = m1c1;
39+
components[1] = m1c2;
40+
components[2] = m1c3;
41+
measure.recordMeasure(components);
42+
43+
components[0] = m2c1;
44+
components[1] = m2c2;
45+
components[2] = m2c3;
46+
measure.recordMeasure(components);
47+
48+
components[0] = m3c1;
49+
components[1] = m3c2;
50+
components[2] = m3c3;
51+
measure.recordMeasure(components);
52+
53+
final var c1Avg = measure.averagesPerComponent()[0];
54+
final var c2Avg = measure.averagesPerComponent()[1];
55+
final var stdVarForC1 = Math
56+
.sqrt((Math.pow(m1c1 - c1Avg, 2) + Math.pow(m2c1 - c1Avg, 2) + Math.pow(m3c1 - c1Avg, 2)) / (3 - 1));
57+
final var stdVarForC2 = Math
58+
.sqrt((Math.pow(m1c2 - c2Avg, 2) + Math.pow(m2c2 - c2Avg, 2) + Math.pow(m3c2 - c2Avg, 2)) / (3 - 1));
59+
60+
assertEquals(stdVarForC1, Compute.standardDeviation(measure, 0), 0.0001,
61+
"Standard Deviation did not match the expected value");
62+
assertEquals(stdVarForC2, Compute.standardDeviation(measure, 1), 0.0001,
63+
"Standard Deviation did not match the expected value");
64+
assertEquals(0, Compute.standardDeviation(measure, 2), 0.0001,
65+
"Standard Deviation did not match the expected value");
66+
}
67+
}

measure/src/main/java/net/laprun/sustainability/power/measure/OngoingPowerMeasure.java

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import java.time.Duration;
44
import java.util.BitSet;
55
import java.util.Objects;
6-
7-
import org.apache.commons.math3.util.FastMath;
6+
import java.util.Optional;
87

98
import net.laprun.sustainability.power.SensorMetadata;
109

@@ -124,36 +123,15 @@ public double[] averagesPerComponent() {
124123
return averages;
125124
}
126125

127-
public StdDev standardDeviations() {
128-
final var cardinality = sensorMetadata.componentCardinality();
129-
final var stdDevs = new double[cardinality];
130-
nonZeroComponents.stream()
131-
.parallel()
132-
.forEach(component -> stdDevs[component] = standardDeviation(component));
133-
134-
final double aggregate = maxTotal == 0 ? 0 : standardDeviation(totalIndex);
135-
return new StdDev(aggregate, stdDevs);
136-
}
137-
138-
private double standardDeviation(int component) {
139-
final var values = measures[component];
140-
if (samples <= 1) {
141-
return 0.0;
142-
}
143-
final double mean = averages[component];
144-
double geometricDeviationTotal = 0.0;
145-
for (int index = 0; index < samples; index++) {
146-
double deviation = values[index] - mean;
147-
geometricDeviationTotal += (deviation * deviation);
148-
}
149-
return FastMath.sqrt(geometricDeviationTotal / (samples - 1));
150-
}
151-
152126
@Override
153-
public double[] getMeasuresFor(int component) {
154-
final var dest = new double[samples];
155-
System.arraycopy(measures[component], 0, dest, 0, samples);
156-
return dest;
127+
public Optional<double[]> getMeasuresFor(int component) {
128+
if (nonZeroComponents.get(component)) {
129+
final var dest = new double[samples];
130+
System.arraycopy(measures[component], 0, dest, 0, samples);
131+
return Optional.of(dest);
132+
} else {
133+
return Optional.empty();
134+
}
157135
}
158136

159137
@Override

measure/src/main/java/net/laprun/sustainability/power/measure/PowerMeasure.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.laprun.sustainability.power.measure;
22

33
import java.time.Duration;
4+
import java.util.Optional;
45

56
import net.laprun.sustainability.power.SensorMetadata;
67

@@ -29,9 +30,8 @@ static String asString(PowerMeasure measure) {
2930
final var durationInSeconds = measure.duration().getSeconds();
3031
final var samples = measure.numberOfSamples();
3132
final var measuredMilliWatts = measure.total();
32-
final var stdDevs = measure.standardDeviations();
33-
return String.format("%s / avg: %s / std dev: %.3f [min: %.3f, max: %.3f] (%ds, %s samples)",
34-
readableWithUnit(measuredMilliWatts), readableWithUnit(measure.average()), stdDevs.aggregate,
33+
return String.format("%s / avg: %s [min: %.3f, max: %.3f] (%ds, %s samples)",
34+
readableWithUnit(measuredMilliWatts), readableWithUnit(measure.average()),
3535
measure.minMeasuredTotal(), measure.maxMeasuredTotal(), durationInSeconds, samples);
3636
}
3737

@@ -59,9 +59,7 @@ default double average() {
5959

6060
double maxMeasuredTotal();
6161

62-
StdDev standardDeviations();
63-
64-
double[] getMeasuresFor(int component);
62+
Optional<double[]> getMeasuresFor(int component);
6563

6664
Analyzer[] analyzers();
6765

measure/src/main/java/net/laprun/sustainability/power/measure/StoppedPowerMeasure.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.laprun.sustainability.power.measure;
22

33
import java.time.Duration;
4+
import java.util.Optional;
45

56
import net.laprun.sustainability.power.SensorMetadata;
67

@@ -13,7 +14,6 @@ public class StoppedPowerMeasure implements PowerMeasure {
1314
private final double min;
1415
private final double max;
1516
private final double[] averages;
16-
private final StdDev standardDeviations;
1717
private final double[][] measures;
1818
private final Analyzer[] analyzers;
1919

@@ -24,12 +24,11 @@ public StoppedPowerMeasure(PowerMeasure powerMeasure) {
2424
this.min = powerMeasure.minMeasuredTotal();
2525
this.max = powerMeasure.maxMeasuredTotal();
2626
this.averages = powerMeasure.averagesPerComponent();
27-
this.standardDeviations = powerMeasure.standardDeviations();
2827
this.samples = powerMeasure.numberOfSamples();
2928
final var cardinality = metadata().componentCardinality();
3029
measures = new double[cardinality][samples];
3130
for (int i = 0; i < cardinality; i++) {
32-
measures[i] = powerMeasure.getMeasuresFor(i);
31+
measures[i] = powerMeasure.getMeasuresFor(i).orElse(null);
3332
}
3433
analyzers = powerMeasure.analyzers();
3534
}
@@ -70,13 +69,8 @@ public SensorMetadata metadata() {
7069
}
7170

7271
@Override
73-
public StdDev standardDeviations() {
74-
return standardDeviations;
75-
}
76-
77-
@Override
78-
public double[] getMeasuresFor(int component) {
79-
return measures[component];
72+
public Optional<double[]> getMeasuresFor(int component) {
73+
return Optional.ofNullable(measures[component]);
8074
}
8175

8276
@Override

measure/src/test/java/net/laprun/sustainability/power/measure/OngoingPowerMeasureTest.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package net.laprun.sustainability.power.measure;
22

3-
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
3+
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.junit.jupiter.api.Assertions.assertEquals;
55

66
import java.util.Map;
@@ -20,7 +20,7 @@ public int componentCardinality() {
2020
};
2121

2222
@Test
23-
void testStatistics() {
23+
void testBasics() {
2424
final var m1c1 = 10.0;
2525
final var m1c2 = 12.0;
2626
final var m1c3 = 0.0;
@@ -52,9 +52,9 @@ void testStatistics() {
5252
components[2] = m3c3;
5353
measure.recordMeasure(components);
5454

55-
assertArrayEquals(new double[] { m1c1, m2c1, m3c1 }, measure.getMeasuresFor(0));
56-
assertArrayEquals(new double[] { m1c2, m2c2, m3c2 }, measure.getMeasuresFor(1));
57-
assertArrayEquals(new double[] { m1c3, m2c3, m3c3 }, measure.getMeasuresFor(2));
55+
assertThat(measure.getMeasuresFor(0)).hasValue(new double[] { m1c1, m2c1, m3c1 });
56+
assertThat(measure.getMeasuresFor(1)).hasValue(new double[] { m1c2, m2c2, m3c2 });
57+
assertThat(measure.getMeasuresFor(2)).isEmpty();
5858

5959
assertEquals(m1c1 + m1c2 + m1c3 + m2c1 + m2c2 + m2c3 + m3c1 + m3c2 + m3c3, measure.total());
6060
assertEquals((m1c1 + m1c2 + m1c3 + m2c1 + m2c2 + m2c3 + m3c1 + m3c2 + m3c3) / 3, measure.average());
@@ -66,17 +66,5 @@ void testStatistics() {
6666
assertEquals((m1c1 + m2c1 + m3c1) / 3, c1Avg);
6767
assertEquals((m1c2 + m2c2 + m3c2) / 3, c2Avg);
6868
assertEquals(0, c3Avg);
69-
70-
final var stdVarForC1 = Math
71-
.sqrt((Math.pow(m1c1 - c1Avg, 2) + Math.pow(m2c1 - c1Avg, 2) + Math.pow(m3c1 - c1Avg, 2)) / (3 - 1));
72-
final var stdVarForC2 = Math
73-
.sqrt((Math.pow(m1c2 - c2Avg, 2) + Math.pow(m2c2 - c2Avg, 2) + Math.pow(m3c2 - c2Avg, 2)) / (3 - 1));
74-
75-
assertEquals(stdVarForC1, measure.standardDeviations().perComponent()[0], 0.0001,
76-
"Standard Deviation did not match the expected value");
77-
assertEquals(stdVarForC2, measure.standardDeviations().perComponent()[1], 0.0001,
78-
"Standard Deviation did not match the expected value");
79-
assertEquals(0, measure.standardDeviations().perComponent()[2], 0.0001,
80-
"Standard Deviation did not match the expected value");
8169
}
8270
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
<module>metadata</module>
7070
<module>measure</module>
7171
<module>server</module>
72+
<module>analysis</module>
7273
</modules>
7374

7475
<dependencyManagement>

0 commit comments

Comments
 (0)