Skip to content

Commit

Permalink
feat: initial measure module to help client-side processing
Browse files Browse the repository at this point in the history
  • Loading branch information
metacosm committed May 23, 2024
1 parent ac6daab commit 51f6f24
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 1 deletion.
40 changes: 40 additions & 0 deletions measure/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>net.laprun.sustainability</groupId>
<artifactId>power-server-parent</artifactId>
<version>0.0.9-SNAPSHOT</version>
</parent>

<artifactId>power-server-measure</artifactId>
<name>power-server : measure</name>
<description>A library to gather and analyze power consumption measures</description>

<dependencies>
<dependency>
<groupId>net.laprun.sustainability</groupId>
<artifactId>power-server-metadata</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>net.laprun.sustainability</groupId>
<artifactId>build-tools</artifactId>
<version>0.0.9-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package net.laprun.sustainability.power.measure;

import java.util.List;

import net.laprun.sustainability.power.SensorMetadata;

public abstract class AbstractPowerMeasure implements PowerMeasure {
private final SensorMetadata sensorMetadata;
private final List<double[]> measures;

protected AbstractPowerMeasure(SensorMetadata sensorMetadata, List<double[]> measures) {
this.sensorMetadata = sensorMetadata;
this.measures = measures;
}

@Override
public List<double[]> measures() {
return measures;
}

@Override
public SensorMetadata metadata() {
return sensorMetadata;
}

public int numberOfSamples() {
return measures.size();
}

double[] getComponentData(int componentIndex) {
return measures.parallelStream().mapToDouble(measure -> measure[componentIndex]).toArray();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.laprun.sustainability.power.measure;

import java.util.ArrayList;

import net.laprun.sustainability.power.SensorMetadata;

public class OngoingPowerMeasure extends AbstractPowerMeasure {
private final long startedAt;
private double minTotal = Double.MAX_VALUE;
private double maxTotal;
private final double[] totals;
private final double[] averages;

public OngoingPowerMeasure(SensorMetadata sensorMetadata, long duration, long frequency) {
super(sensorMetadata, new ArrayList<>((int) (duration / frequency)));
startedAt = System.currentTimeMillis();
final var numComponents = metadata().componentCardinality();
totals = new double[numComponents];
averages = new double[numComponents];
}

public void recordMeasure(double[] components) {
final var recorded = new double[components.length];
System.arraycopy(components, 0, recorded, 0, components.length);
final var previousSize = numberOfSamples();
measures().add(recorded);

for (int i = 0; i < recorded.length; i++) {
totals[i] += recorded[i];
averages[i] = averages[i] == 0 ? recorded[i]
: (previousSize * averages[i] + recorded[i]) / numberOfSamples();
}

// record min / max totals
final var recordedTotal = PowerMeasure.sumOfComponents(recorded);
if (recordedTotal < minTotal) {
minTotal = recordedTotal;
}
if (recordedTotal > maxTotal) {
maxTotal = recordedTotal;
}
}

@Override
public double total() {
return PowerMeasure.sumOfComponents(totals);
}

public long duration() {
return System.currentTimeMillis() - startedAt;
}

@Override
public double minMeasuredTotal() {
return minTotal;
}

@Override
public double maxMeasuredTotal() {
return maxTotal;
}

@Override
public double[] averagesPerComponent() {
return averages;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package net.laprun.sustainability.power.measure;

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;

import net.laprun.sustainability.power.SensorMetadata;

public interface PowerMeasure {
int numberOfSamples();

long duration();

default double average() {
return total() / numberOfSamples();
}

double total();

SensorMetadata metadata();

double[] averagesPerComponent();

double minMeasuredTotal();

double maxMeasuredTotal();

static double sumOfComponents(double[] recorded) {
var componentSum = 0.0;
for (double value : recorded) {
componentSum += value;
}
return componentSum;
}

default StdDev standardDeviations() {
final var cardinality = metadata().componentCardinality();
final var stdDevs = new double[cardinality];
final var aggregate = new double[1];
final var samples = numberOfSamples() - 1; // unbiased so we remove one sample
final var sqrdAverages = Arrays.stream(averagesPerComponent()).map(m -> m * m).toArray();
final var sqrdAverage = average() * average();
// need to compute the average of variances then square root that to get the "aggregate" standard deviation,
// see: https://stats.stackexchange.com/a/26647
// "vectorize" computation of variances: compute the variance for each component in parallel
IntStream.range(0, cardinality).parallel()
// compute variances for each component of the measure
.forEach(component -> {
final var sumOfSquares = measures().stream().parallel().peek(m -> {
// compute the std dev for total measure
final var total = sumOfComponents(m);
aggregate[0] += total * total;
}).mapToDouble(m -> m[component] * m[component]).sum();
stdDevs[component] = stdDev(sumOfSquares, sqrdAverages[component], samples);
aggregate[0] = stdDev(aggregate[0], sqrdAverage, samples);
});
return new StdDev(aggregate[0], stdDevs);
}

private static double stdDev(double sumOfSquares, double squaredAvg, int samples) {
return Math.sqrt((sumOfSquares / samples) - (((samples + 1) * squaredAvg) / samples));
}

/**
* Records the standard deviations for the aggregated energy comsumption value (as returned by {@link #total()}) and
* per component
*
* @param aggregate
* @param perComponent
*/
record StdDev(double aggregate, double[] perComponent) {
}

static String asString(PowerMeasure measure) {
final var durationInSeconds = measure.duration() / 1000;
final var samples = measure.numberOfSamples();
final var measuredMilliWatts = measure.total();
final var stdDevs = measure.standardDeviations();
return String.format("%s / avg: %s / std dev: %.3f [min: %.3f, max: %.3f] (%ds, %s samples)",
readableWithUnit(measuredMilliWatts), readableWithUnit(measure.average()), stdDevs.aggregate,
measure.minMeasuredTotal(), measure.maxMeasuredTotal(), durationInSeconds, samples);
}

static String readableWithUnit(double milliWatts) {
String unit = milliWatts >= 1000 ? "W" : "mW";
double power = milliWatts >= 1000 ? milliWatts / 1000 : milliWatts;
return String.format("%.3f%s", power, unit);
}

List<double[]> measures();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.laprun.sustainability.power.measure;

public class StoppedPowerMeasure extends AbstractPowerMeasure {
private final long duration;
private final double total;
private final double min;
private final double max;
private final double[] averages;

public StoppedPowerMeasure(PowerMeasure powerMeasure) {
super(powerMeasure.metadata(), powerMeasure.measures());
this.duration = powerMeasure.duration();
this.total = powerMeasure.total();
this.min = powerMeasure.minMeasuredTotal();
this.max = powerMeasure.maxMeasuredTotal();
this.averages = powerMeasure.averagesPerComponent();
}

@Override
public long duration() {
return duration;
}

@Override
public double minMeasuredTotal() {
return min;
}

@Override
public double maxMeasuredTotal() {
return max;
}

@Override
public double total() {
return total;
}

@Override
public double[] averagesPerComponent() {
return averages;
}
}
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@

<modules>
<module>build-tools</module>
<module>server</module>
<module>metadata</module>
<module>measure</module>
<module>server</module>
</modules>

<dependencyManagement>
Expand Down

0 comments on commit 51f6f24

Please sign in to comment.