Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: extract scheduling to new PowerMeasurer to simplify sensors #10

Merged
merged 1 commit into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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