Skip to content

Commit

Permalink
refactor: extract scheduling to new PowerMeasurer to simplify sensors
Browse files Browse the repository at this point in the history
Also separate API from SPI classes.
  • Loading branch information
metacosm committed Nov 2, 2023
1 parent fa4ec5f commit f7bc200
Show file tree
Hide file tree
Showing 19 changed files with 298 additions and 297 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.quarkiverse.power.deployment;

import io.quarkiverse.power.deployment.devui.commands.PowerCommands;
import io.quarkiverse.power.runtime.PowerSensorProducer;
import io.quarkiverse.power.runtime.PowerMeasurer;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -20,7 +20,6 @@ FeatureBuildItem feature() {
@BuildStep(onlyIf = IsDevelopment.class)
void addConsoleCommands(BuildProducer<ConsoleCommandBuildItem> commands) {
// register dev console commands
final var producer = new PowerSensorProducer();
commands.produce(new ConsoleCommandBuildItem(new PowerCommands(producer.sensor())));
commands.produce(new ConsoleCommandBuildItem(new PowerCommands(PowerMeasurer.instance())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import org.aesh.command.*;
import org.aesh.command.invocation.CommandInvocation;

import io.quarkiverse.power.runtime.PowerSensor;
import io.quarkiverse.power.runtime.PowerMeasurer;
import io.quarkiverse.power.runtime.SensorMeasure;

@GroupCommandDefinition(name = "power", description = "Power consumption commands", generateHelp = true)
@SuppressWarnings("rawtypes")
public class PowerCommands implements GroupCommand {
private final PowerSensor<?> sensor;
private final PowerMeasurer<? extends SensorMeasure> sensor;

public PowerCommands(PowerSensor<?> sensor) {
public PowerCommands(PowerMeasurer<? extends SensorMeasure> sensor) {
this.sensor = sensor;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@
import org.aesh.command.invocation.CommandInvocation;
import org.aesh.command.option.Option;

import io.quarkiverse.power.runtime.PowerSensor;
import io.quarkiverse.power.runtime.PowerMeasurer;
import io.quarkiverse.power.runtime.SensorMeasure;
import io.quarkus.deployment.console.QuarkusCommand;

@CommandDefinition(name = "start", description = "Starts measuring power consumption of the current application")
@SuppressWarnings("rawtypes")
public class StartCommand extends QuarkusCommand {
private final PowerSensor<?> sensor;
private final PowerMeasurer<? extends SensorMeasure> sensor;

@Option(name = "stopAfter", shortName = 's', description = "Automatically stop the measures after the specified duration in seconds", defaultValue = "-1")
private long duration;

@Option(name = "frequency", shortName = 'f', description = "The frequency at which measurements should be taken, in milliseconds", defaultValue = "1000")
private long frequency;

public StartCommand(PowerSensor<?> sensor) {
public StartCommand(PowerMeasurer<? extends SensorMeasure> sensor) {
this.sensor = sensor;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@
import org.aesh.command.CommandResult;
import org.aesh.command.invocation.CommandInvocation;

import io.quarkiverse.power.runtime.PowerSensor;
import io.quarkiverse.power.runtime.PowerMeasurer;
import io.quarkiverse.power.runtime.SensorMeasure;
import io.quarkus.deployment.console.QuarkusCommand;

@CommandDefinition(name = "stop", description = "Stops power measurement and outputs accumulated power since measures were started")
@SuppressWarnings("rawtypes")
public class StopCommand extends QuarkusCommand {

private final PowerSensor sensor;
private final PowerMeasurer<? extends SensorMeasure> sensor;

public StopCommand(PowerSensor<?> sensor) {
public StopCommand(PowerMeasurer<? extends SensorMeasure> sensor) {
this.sensor = sensor;
}

@Override
public CommandResult doExecute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
sensor.stop();
sensor.outputConsumptionSinceStarted(commandInvocation::println);
sensor.stop(commandInvocation::println);

return CommandResult.SUCCESS;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.quarkiverse.power.runtime;

public interface PowerMeasure<M extends SensorMeasure> extends SensorMeasure {
int numberOfSamples();

long duration();

M sensorMeasure();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.quarkiverse.power.runtime;

import java.lang.management.ManagementFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import com.sun.management.OperatingSystemMXBean;

import io.quarkiverse.power.runtime.sensors.IncrementableMeasure;
import io.quarkiverse.power.runtime.sensors.OngoingPowerMeasure;
import io.quarkiverse.power.runtime.sensors.PowerSensor;
import io.quarkiverse.power.runtime.sensors.PowerSensorProducer;

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

static {
osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
}

private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> scheduled;
private final PowerSensor<M> sensor;
private OngoingPowerMeasure<M> measure;

private final static PowerMeasurer<? extends SensorMeasure> instance = new PowerMeasurer<>(
PowerSensorProducer.determinePowerSensor());

public static PowerMeasurer<? extends SensorMeasure> instance() {
return instance;
}

public PowerMeasurer(PowerSensor<M> sensor) {
this.sensor = sensor;
}

public void start(long duration, long frequency, PowerSensor.Writer out) throws Exception {
if (measure == null) {
measure = sensor.start(duration, frequency, out);

if (duration > 0) {
executor.schedule(() -> stop(out), duration, TimeUnit.SECONDS);
}

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

private void update(PowerSensor.Writer out) {
sensor.update(measure, out);
measure.incrementSamples();
}

public PowerMeasure<M> stop(PowerSensor.Writer out) {
if (measure != null) {
sensor.stop();
scheduled.cancel(true);
}
outputConsumptionSinceStarted(out);
return measure;
}

public PowerMeasure<M> current() {
return measure;
}

private void outputConsumptionSinceStarted(PowerSensor.Writer out) {
out = out == null ? System.out::println : out;
out.println("Consumed " + measure.total() + " mW over " + (measure.duration() / 1000)
+ " seconds (" + measure.numberOfSamples() + " samples)");
sensor.additionalInfo(out);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.quarkiverse.power.runtime;

import java.util.Optional;

public interface SensorMeasure {
String CPU = "cpu";
String GPU = "gpu";
String TOTAL = "total";

double cpu();

default Optional<Double> gpu() {
return Optional.empty();
}

default Optional<Double> byKey(String key) {
return switch (key) {
case CPU -> Optional.of(cpu());
case GPU -> gpu();
case TOTAL -> Optional.of(total());
default -> Optional.empty();
};
}

default double total() {
return cpu() + gpu().orElse(0.0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkiverse.power.runtime.sensors;

import io.quarkiverse.power.runtime.SensorMeasure;

public interface IncrementableMeasure extends SensorMeasure {

void addCPU(double v);

default void addGPU(double v) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.quarkiverse.power.runtime.sensors;

import java.util.Optional;

import io.quarkiverse.power.runtime.PowerMeasure;

public class OngoingPowerMeasure<M extends IncrementableMeasure>
implements IncrementableMeasure, PowerMeasure<M> {
private final M measure;
private final long startedAt;
private int samplesNb;

public OngoingPowerMeasure(M measure) {
startedAt = System.currentTimeMillis();
samplesNb = 0;
this.measure = measure;
}

public void incrementSamples() {
samplesNb++;
}

@Override
public double cpu() {
return measure.cpu();
}

@Override
public Optional<Double> gpu() {
return measure.gpu();
}

@Override
public Optional<Double> byKey(String key) {
return measure.byKey(key);
}

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

public int numberOfSamples() {
return samplesNb;
}

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

@Override
public void addCPU(double v) {
measure.addCPU(v);
}

@Override
public void addGPU(double v) {
measure.addGPU(v);
}

public M sensorMeasure() {
return measure;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkiverse.power.runtime.sensors;

public interface PowerSensor<T extends IncrementableMeasure> {

OngoingPowerMeasure<T> start(long duration, long frequency, Writer out) throws Exception;

default void stop() {
}

void update(OngoingPowerMeasure<T> ongoingMeasure, Writer out);

default void additionalInfo(Writer out) {
}

interface Writer {
void println(String message);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.quarkiverse.power.runtime;
package io.quarkiverse.power.runtime.sensors;

import jakarta.enterprise.inject.Produces;
import jakarta.inject.Singleton;
Expand All @@ -10,6 +10,10 @@
public class PowerSensorProducer {
@Produces
public PowerSensor<?> sensor() {
return determinePowerSensor();
}

public static PowerSensor<? extends IncrementableMeasure> determinePowerSensor() {
final var originalOSName = System.getProperty("os.name");
String osName = originalOSName.toLowerCase();

Expand Down
Loading

0 comments on commit f7bc200

Please sign in to comment.