Skip to content
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,13 +1,11 @@
package net.laprun.sustainability.power.sensors;

import java.util.Map;
import java.util.Set;

import org.eclipse.microprofile.config.inject.ConfigProperty;

import io.quarkus.logging.Log;
import net.laprun.sustainability.power.SensorMetadata;
import net.laprun.sustainability.power.SensorUnit;

public abstract class AbstractPowerSensor implements PowerSensor {
protected final Measures measures;
Expand All @@ -16,7 +14,6 @@ public abstract class AbstractPowerSensor implements PowerSensor {
@ConfigProperty(name = "power-server.enable-cpu-share-sampling", defaultValue = "false")
protected boolean cpuSharesEnabled;
private SensorMetadata metadata;
private int externalCPUShareComponentIndex = -1;

public AbstractPowerSensor(Measures measures) {
this.measures = measures;
Expand All @@ -30,14 +27,6 @@ public AbstractPowerSensor() {
public SensorMetadata metadata() {
if (metadata == null) {
metadata = nativeMetadata();
if (cpuSharesEnabled) {
metadata = SensorMetadata.from(metadata)
.withNewComponent(EXTERNAL_CPU_SHARE_COMPONENT_NAME,
"CPU share estimate based on currently configured strategy used in CPUShare", false,
SensorUnit.decimalPercentage)
.build();
externalCPUShareComponentIndex = metadata.metadataFor(EXTERNAL_CPU_SHARE_COMPONENT_NAME).index();
}
}
return metadata;
}
Expand All @@ -54,10 +43,6 @@ public void enableCPUShareSampling(boolean enable) {
cpuSharesEnabled = enable;
}

protected int externalCPUShareComponentIndex() {
return externalCPUShareComponentIndex;
}

@Override
public RegisteredPID register(long pid) {
Log.debugf("Registered pid: %d", pid);
Expand Down Expand Up @@ -98,22 +83,17 @@ public Set<String> getRegisteredPIDs() {
protected abstract void doStart();

@Override
public Measures update(Long tick, Map<String, Double> cpuShares) {
public Measures update(long tick) {
final long newUpdateStartEpoch = System.currentTimeMillis();
Log.debugf("Sensor update last called: %dms ago", newUpdateStartEpoch - lastUpdateEpoch);
final var measures = doUpdate(lastUpdateEpoch, newUpdateStartEpoch, cpuShares);
final var measures = doUpdate(lastUpdateEpoch, newUpdateStartEpoch);
lastUpdateEpoch = newUpdateStartEpoch;
return measures;
}

@Override
public Measures update(long tick) {
return update(tick, Map.of());
}

protected long lastUpdateEpoch() {
return lastUpdateEpoch;
}

abstract protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch, Map<String, Double> cpuShares);
abstract protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package net.laprun.sustainability.power.sensors;

import java.util.Map;
import java.util.Set;

import net.laprun.sustainability.power.SensorMetadata;
Expand All @@ -9,9 +8,6 @@
* A representation of a power-consumption sensor.
*/
public interface PowerSensor {
String EXTERNAL_CPU_SHARE_COMPONENT_NAME = "externalCpuShare";
Double MISSING_CPU_SHARE = -1.0;

/**
* Whether the sensor supports process attribution of power, i.e. is measured power imputed to each process or does
* attribution need to be performed externally to the sensor.
Expand Down Expand Up @@ -73,11 +69,8 @@ default void stop() {
*
* @param tick an ordinal value tracking the number of recorded measures being taken by the sensor since it started
* measuring power consumption
* @param cpuShares externally provided (if available) cpu attribution for each process id
* @return the {@link Measures} object recording the measures this sensor has taken since it started measuring
*/
Measures update(Long tick, Map<String, Double> cpuShares);

Measures update(long tick);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
import java.util.function.BiConsumer;

import io.quarkus.logging.Log;
import net.laprun.sustainability.power.SensorMeasure;
import net.laprun.sustainability.power.SensorMetadata;
import net.laprun.sustainability.power.measures.NoDurationSensorMeasure;
import net.laprun.sustainability.power.sensors.AbstractPowerSensor;
import net.laprun.sustainability.power.sensors.Measures;
import net.laprun.sustainability.power.sensors.RegisteredPID;

/**
* A sensor using Intel's RAPL accessed via Linux' powercap system.
Expand Down Expand Up @@ -140,36 +138,16 @@ protected SensorMetadata nativeMetadata() {
}

@Override
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch, Map<String, Double> cpuShares) {
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch) {
final var measure = new double[metadata().componentCardinality()];
readAndRecordSensor((value, index) -> {
measure[index] = computePowerInMilliWatts(index, value, newUpdateStartEpoch);
measure[index + rawOffset] = value;
},
newUpdateStartEpoch);

final var needMultipleMeasures = wantsCPUShareSamplingEnabled() && externalCPUShareComponentIndex() > 0;
final var single = new NoDurationSensorMeasure(measure, lastUpdateEpoch, newUpdateStartEpoch);
measures.trackedPIDs().forEach(pid -> {
final SensorMeasure m;
if (needMultipleMeasures) {
double cpuShare;
if (RegisteredPID.SYSTEM_TOTAL_REGISTERED_PID.equals(pid)) {
cpuShare = 1.0;
} else {
cpuShare = cpuShares.getOrDefault(pid.pidAsString(), 0.0);
}
// todo: avoid copying array, external cpu share should be recorded as a separate value, not a component maybe?
// copy array
final var copy = new double[measure.length];
System.arraycopy(measure, 0, copy, 0, measure.length);
copy[externalCPUShareComponentIndex()] = cpuShare;
m = new NoDurationSensorMeasure(copy, lastUpdateEpoch, newUpdateStartEpoch);
} else {
m = single;
}
measures.record(pid, m);
});
measures.trackedPIDs().forEach(pid -> measures.record(pid, single));

return measures;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ private static class Section {
boolean done;
}

Measures extractPowerMeasure(InputStream powerMeasureInput, long lastUpdateEpoch, long newUpdateEpoch,
Map<String, Double> cpuShares) {
Measures extractPowerMeasure(InputStream powerMeasureInput, long lastUpdateEpoch, long newUpdateEpoch) {
if (Log.isDebugEnabled()) {
final var start = System.currentTimeMillis();
Log.debugf("powermetrics measure extraction last called %dms ago", (start - lastCalled));
Expand Down Expand Up @@ -214,12 +213,6 @@ Measures extractPowerMeasure(InputStream powerMeasureInput, long lastUpdateEpoch
final var endMs = newUpdateEpoch;
final var durationMs = duration;

// handle external cpu share if enabled
if (cpuShares != null && !cpuShares.isEmpty()) {
measures.trackedPIDsAsString().forEach(name -> powerComponents.put(EXTERNAL_CPU_SHARE_COMPONENT_NAME,
cpuShares.getOrDefault(name, MISSING_CPU_SHARE)));
}

// handle total system measure separately
final var systemTotalMeasure = getSystemTotalMeasure(metadata, powerComponents);
recordMeasure(RegisteredPID.SYSTEM_TOTAL_REGISTERED_PID, systemTotalMeasure, startMs, endMs, durationMs);
Expand All @@ -244,7 +237,7 @@ private static double[] getSystemTotalMeasure(SensorMetadata metadata, Map<Strin
final var measure = new double[metadata.componentCardinality()];
metadata.components().forEach((name, cm) -> {
final var index = cm.index();
final var value = CPU_SHARE.equals(name) || EXTERNAL_CPU_SHARE_COMPONENT_NAME.equals(name) ? 1.0
final var value = CPU_SHARE.equals(name) ? 1.0
: powerComponents.getOrDefault(name, 0).doubleValue();
measure[index] = value;
});
Expand All @@ -253,8 +246,8 @@ private static double[] getSystemTotalMeasure(SensorMetadata metadata, Map<Strin
}

@Override
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch, Map<String, Double> cpuShares) {
return extractPowerMeasure(getInputStream(), lastUpdateEpoch, newUpdateStartEpoch, cpuShares);
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch) {
return extractPowerMeasure(getInputStream(), lastUpdateEpoch, newUpdateStartEpoch);
}

protected abstract InputStream getInputStream();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.laprun.sustainability.power.sensors.macos.powermetrics;

import java.io.InputStream;
import java.util.Map;

import net.laprun.sustainability.power.sensors.Measures;

Expand All @@ -21,9 +20,9 @@ public ResourceMacOSPowermetricsSensor(String resourceName) {
}

@Override
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch, Map<String, Double> cpuShares) {
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch) {
// use the expected start measured time (if provided) for the measure instead of using the provided current epoch
return super.doUpdate(start != -1 ? start : lastUpdateEpoch, newUpdateStartEpoch, cpuShares);
return super.doUpdate(start != -1 ? start : lastUpdateEpoch, newUpdateStartEpoch);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static net.laprun.sustainability.power.SensorUnit.mW;

import java.util.List;
import java.util.Map;

import net.laprun.sustainability.power.SensorMetadata;
import net.laprun.sustainability.power.measures.NoDurationSensorMeasure;
Expand Down Expand Up @@ -39,7 +38,7 @@ public void doStart() {
}

@Override
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch, Map<String, Double> cpuShares) {
protected Measures doUpdate(long lastUpdateEpoch, long newUpdateStartEpoch) {
measures.trackedPIDs().forEach(pid -> measures.record(pid,
new NoDurationSensorMeasure(new double[] { Math.random() }, lastUpdateEpoch, newUpdateStartEpoch)));
return measures;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void wattComputationShouldWork() throws Exception {
sensor.start();
Thread.sleep(10); // ensure we get enough time between the measure performed during start and the first update
final var pid = sensor.register(1234L);
final var measures = sensor.update(1L, Map.of());
final var measures = sensor.update(1L);
final var components = measures.getOrDefault(pid).components();
assertEquals(2, components.length);
assertEquals(2, raplFile.callCount());
Expand All @@ -113,25 +113,6 @@ void wattComputationShouldWork() throws Exception {
assertEquals(20000, components[1]);
}

@Test
void shouldIncludeCPUShareIfRequested() throws Exception {
final var raplFile = new TestRAPLFile(10000L, 20000L, 30000L);
final var sensor = new TestIntelRAPLSensor(new TreeMap<>(Map.of("sensor", raplFile)));
sensor.enableCPUShareSampling(true);
sensor.start();
final var pid = sensor.register(1234L);
double cpuShare = 0.3;
final var measures = sensor.update(1L, Map.of("1234", cpuShare));
final var components = measures.getOrDefault(pid).components();
assertEquals(3, components.length);
assertEquals(2, raplFile.callCount());
final var interval = raplFile.measureTimeFor(1) - raplFile.measureTimeFor(0);
final var expected = (double) (raplFile.valueAt(1) - raplFile.valueAt(0)) / interval;
assertEquals(expected, components[0]);
assertEquals(20000, components[1]);
assertEquals(cpuShare, components[2]);
}

@SuppressWarnings("SameParameterValue")
private SensorMetadata loadMetadata(String... fileNames) {
Class<? extends IntelRAPLSensorTest> clazz = getClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Map;

import org.junit.jupiter.api.Test;

import net.laprun.sustainability.power.SensorMetadata;
Expand Down Expand Up @@ -67,7 +65,7 @@ void extractPowerMeasureForM4() {

final var cpu = metadata.metadataFor(MacOSPowermetricsSensor.CPU);
// re-open the stream to read the measure this time
final var measure = sensor.update(0L, Map.of());
final var measure = sensor.update(0L);

final var totalCPUPower = 420;
final var totalCPUTime = 1287.34;
Expand All @@ -86,7 +84,7 @@ void checkTotalPowerMeasureEvenWhenRegisteredProcessIsNotFound() {
sensor.register(-666);

// re-open the stream to read the measure this time
final var measure = sensor.update(0L, Map.of());
final var measure = sensor.update(0L);

assertEquals(0, getTotalSystemComponent(measure, metadata, MacOSPowermetricsSensor.ANE));
assertEquals(19, getTotalSystemComponent(measure, metadata, MacOSPowermetricsSensor.DRAM));
Expand All @@ -111,7 +109,7 @@ void extractionShouldWorkForLowProcessIds() {

final var cpu = metadata.metadataFor(MacOSPowermetricsSensor.CPU);
// re-open the stream to read the measure this time
final var measure = sensor.update(0L, Map.of());
final var measure = sensor.update(0L);
// Process CPU power should be equal to sample ms/s divided for process (here: 116.64) by total samples (1222.65) times total CPU power
var pidCPUShare = 116.64 / 1222.65;
assertEquals(pidCPUShare * 211, getComponent(measure, pid0, cpu));
Expand All @@ -128,7 +126,7 @@ private static void checkPowerMeasure(String testFileName, float total, String t
final var pid2 = sensor.register(391);

// re-open the stream to read the measure this time
final var measure = sensor.update(0L, Map.of());
final var measure = sensor.update(0L);
final var totalMeasureMetadata = metadata.metadataFor(totalMeasureName);
final var pid1CPUShare = 23.88 / 1222.65;
assertEquals((pid1CPUShare * total), getComponent(measure, pid1, totalMeasureMetadata));
Expand Down