Skip to content

Commit

Permalink
feat: add warm-up support (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
metacosm committed Nov 7, 2023
1 parent bba6ca2 commit 10043aa
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

public class PowerMeasurer<M extends IncrementableMeasure> {
private static final OperatingSystemMXBean osBean;

static {
osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
// take two measures to avoid initial zero values
Expand All @@ -26,6 +27,7 @@ public class PowerMeasurer<M extends IncrementableMeasure> {
private final PowerSensor<M> sensor;
private OngoingPowerMeasure<M> measure;
private StoppedPowerMeasure<M> lastMeasure;
private StoppedPowerMeasure<M> baseline;

private final static PowerMeasurer<? extends SensorMeasure> instance = new PowerMeasurer<>(
PowerSensorProducer.determinePowerSensor());
Expand All @@ -52,20 +54,36 @@ public boolean isRunning() {
return measure != null;
}

public void start(long duration, long frequency, PowerSensor.Writer out) throws Exception {
if (!isRunning()) {
measure = sensor.start(duration, frequency, out);
public void start(long durationInSeconds, long frequencyInMilliseconds, PowerSensor.Writer out) throws Exception {
start(durationInSeconds, frequencyInMilliseconds, false, out);
}

if (duration > 0) {
executor.schedule(() -> stop(out), duration, TimeUnit.SECONDS);
void start(long durationInSeconds, long frequencyInMilliseconds, boolean skipBaseline, PowerSensor.Writer out)
throws Exception {
if (!isRunning()) {
if (!skipBaseline && baseline == null) {
out.println("Establishing baseline for 30s, please do not use your application until done.");
out.println("Power measurement will start as configured after this initial measure is done.");
doStart(30, 1000, out, () -> baselineDone(durationInSeconds, frequencyInMilliseconds, out));
} else {
doStart(durationInSeconds, frequencyInMilliseconds, out, () -> stop(out));
}

scheduled = executor.scheduleAtFixedRate(() -> update(out),
0, frequency,
TimeUnit.MILLISECONDS);
}
}

private void doStart(long duration, long frequency, PowerSensor.Writer out, Runnable doneAction) throws Exception {
measure = sensor.start(duration, frequency, out);

if (duration > 0) {
executor.schedule(doneAction, duration, TimeUnit.SECONDS);
}

scheduled = executor.scheduleAtFixedRate(() -> update(out),
0, frequency,
TimeUnit.MILLISECONDS);
}

private void update(PowerSensor.Writer out) {
sensor.update(measure, out);
measure.incrementSamples();
Expand All @@ -75,12 +93,28 @@ public void stop(PowerSensor.Writer out) {
if (isRunning()) {
sensor.stop();
scheduled.cancel(true);
outputConsumptionSinceStarted(out);
outputConsumptionSinceStarted(out, false);
lastMeasure = new StoppedPowerMeasure<>(measure);
measure = null;
}
}

private void baselineDone(long durationInSeconds, long frequencyInMilliseconds, PowerSensor.Writer out) {
if (isRunning()) {
sensor.stop();
scheduled.cancel(true);
outputConsumptionSinceStarted(out, true);
baseline = new StoppedPowerMeasure<>(measure);
out.println("Baseline established! You can now interact with your application normally.");
measure = null;
try {
doStart(durationInSeconds, frequencyInMilliseconds, out, () -> stop(out));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

public PowerMeasure<M> current() {
// use the ongoing power measure if it exists
if (measure == null) {
Expand All @@ -95,10 +129,20 @@ public PowerMeasure<M> current() {
}
}

private void outputConsumptionSinceStarted(PowerSensor.Writer out) {
private void outputConsumptionSinceStarted(PowerSensor.Writer out, boolean isBaseline) {
out = out == null ? System.out::println : out;
out.println("Consumed " + measure.total() + " mW over " + (measure.duration() / 1000)
final var durationInSeconds = measure.duration() / 1000;
final var title = isBaseline ? "Baseline power: " : "Measured power: ";
out.println(title + getReadablePower(measure) + " over " + durationInSeconds
+ " seconds (" + measure.numberOfSamples() + " samples)");
sensor.additionalInfo(measure, out);
if (!isBaseline) {
sensor.additionalInfo(measure, out);
out.println("Baseline power was " + getReadablePower(baseline));
}
}

private static String getReadablePower(PowerMeasure<?> measure) {
final var measuredMilliWatts = measure.total();
return measuredMilliWatts >= 1000 ? (measuredMilliWatts / 1000) + " W" : measuredMilliWatts + "mW";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void startShouldAccumulateOverSpecifiedDurationAndStop() throws Exception {
sensor = Mockito.spy(sensor);
final var measurer = new PowerMeasurer<>(sensor);

measurer.start(1, 100, null);
measurer.start(1, 100, true, null);
Thread.sleep(2000);
final var measure = measurer.current();
assertEquals(10, measure.numberOfSamples());
Expand Down

0 comments on commit 10043aa

Please sign in to comment.