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

Added thermal model for hosts #238

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
zohaib-c marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public record HostSystemStats(
Instant bootTime,
double powerDraw,
double energyUsage,
double cpuTemperature,
int guestsTerminated,
int guestsRunning,
int guestsError,
Expand Down
zohaib-c marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public class SimHost(
localBootTime,
machine.psu.powerDraw,
machine.psu.energyUsage,
machine.psu.cpuTemperature,
terminated,
running,
error,
Expand Down
zohaib-c marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ public class ComputeMetricReader(
_cpuLostTime = table.cpuLostTime
_powerDraw = table.powerDraw
_energyUsage = table.energyUsage
_cpuTemperature = table.cpuTemperature
_carbonIntensity = table.carbonIntensity
_carbonEmission = table.carbonEmission
_uptime = table.uptime
Expand Down Expand Up @@ -328,6 +329,10 @@ public class ComputeMetricReader(
private var _energyUsage = 0.0
private var previousPowerTotal = 0.0

override val cpuTemperature: Double
get() = _cpuTemperature
private var _cpuTemperature = 0.0

override val carbonIntensity: Double
get() = _carbonIntensity
private var _carbonIntensity = 0.0
Expand Down Expand Up @@ -378,6 +383,7 @@ public class ComputeMetricReader(
_cpuLostTime = hostCpuStats.lostTime
_powerDraw = hostSysStats.powerDraw
_energyUsage = hostSysStats.energyUsage
_cpuTemperature = hostSysStats.cpuTemperature
_carbonIntensity = carbonTrace.getCarbonIntensity(timestampAbsolute)

_carbonEmission = carbonIntensity * (energyUsage / 3600000.0) // convert energy usage from J to kWh
Expand Down Expand Up @@ -412,6 +418,7 @@ public class ComputeMetricReader(

_powerDraw = 0.0
_energyUsage = 0.0
_cpuTemperature = 0.0
_carbonIntensity = 0.0
_carbonEmission = 0.0
}
Expand Down
zohaib-c marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -152,34 +152,38 @@ public class ParquetHostDataWriter(path: File, bufferSize: Int) :
consumer.addDouble(data.energyUsage)
consumer.endField("energy_usage", 19)

consumer.startField("carbon_intensity", 20)
consumer.startField("cpuTemperature", 20)
consumer.addDouble(data.cpuTemperature)
consumer.endField("cpuTemperature", 20)

consumer.startField("carbon_intensity", 21)
consumer.addDouble(data.carbonIntensity)
consumer.endField("carbon_intensity", 20)
consumer.endField("carbon_intensity", 21)

consumer.startField("carbon_emission", 21)
consumer.startField("carbon_emission", 22)
consumer.addDouble(data.carbonEmission)
consumer.endField("carbon_emission", 21)
consumer.endField("carbon_emission", 22)

consumer.startField("uptime", 22)
consumer.startField("uptime", 23)
consumer.addLong(data.uptime)
consumer.endField("uptime", 22)
consumer.endField("uptime", 23)

consumer.startField("downtime", 23)
consumer.startField("downtime", 24)
consumer.addLong(data.downtime)
consumer.endField("downtime", 23)
consumer.endField("downtime", 24)

val bootTime = data.bootTime
if (bootTime != null) {
consumer.startField("boot_time", 24)
consumer.startField("boot_time", 25)
consumer.addLong(bootTime.toEpochMilli())
consumer.endField("boot_time", 24)
consumer.endField("boot_time", 25)
}

val bootTimeAbsolute = data.bootTimeAbsolute
if (bootTimeAbsolute != null) {
consumer.startField("boot_time_absolute", 25)
consumer.startField("boot_time_absolute", 26)
consumer.addLong(bootTimeAbsolute.toEpochMilli())
consumer.endField("boot_time_absolute", 25)
consumer.endField("boot_time_absolute", 26)
}

consumer.endMessage()
Expand Down Expand Up @@ -256,6 +260,9 @@ public class ParquetHostDataWriter(path: File, bufferSize: Int) :
Types
.required(PrimitiveType.PrimitiveTypeName.DOUBLE)
.named("energy_usage"),
Types
.required(PrimitiveType.PrimitiveTypeName.DOUBLE)
.named("cpuTemperature"),
Types
.required(PrimitiveType.PrimitiveTypeName.DOUBLE)
.named("carbon_intensity"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ public interface HostTableReader {
*/
public val energyUsage: Double

/**
* The current temperature of the host in degrees Celsius.
*/
public val cpuTemperature: Double

/**
* The current carbon intensity of the host in gCO2 / kW.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import org.opendc.simulator.compute.model.MemoryUnit
import org.opendc.simulator.compute.model.ProcessingNode
import org.opendc.simulator.compute.model.ProcessingUnit
import org.opendc.simulator.compute.power.getPowerModel
import org.opendc.simulator.compute.thermal.getThermalModel
import java.io.File
import java.io.InputStream
import java.util.SplittableRandom
Expand Down Expand Up @@ -130,6 +131,16 @@ private fun HostJSONSpec.toHostSpecs(
)

val powerModel = getPowerModel(powerModel.modelType, powerModel.power, powerModel.maxPower, powerModel.idlePower)
val thermalModel =
getThermalModel(
thermalModel.modelType,
thermalModel.rHS,
thermalModel.rCase,
thermalModel.minLeakageCurrent,
thermalModel.maxLeakageCurrent,
thermalModel.supplyVoltage,
thermalModel.ambientTemperature,
)

var hostName: String
if (name == null) {
Expand All @@ -144,7 +155,7 @@ private fun HostJSONSpec.toHostSpecs(
hostName,
mapOf("cluster" to clusterId),
machineModel,
SimPsuFactories.simple(powerModel),
SimPsuFactories.simple(powerModel, thermalModel),
)
hostId++

Expand Down
zohaib-c marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,24 @@ public data class ClusterSpec(
* @param memory The amount of RAM memory available in Byte
* @param powerModel The power model used to determine the power draw of a host
* @param count The power model used to determine the power draw of a host
* @param thermalModel The thermal model used to determine the temperature of a host - defaults to Intel Xeon Platinum 8160
*/
@Serializable
public data class HostJSONSpec(
val name: String? = null,
val cpu: CPUSpec,
val memory: MemorySpec,
val powerModel: PowerModelSpec = PowerModelSpec("linear", 350.0, 400.0, 200.0),
val thermalModel: ThermalModelSpec =
ThermalModelSpec(
"rcmodel",
0.298,
0.00061,
0.00035,
0.0041,
1.8,
22.0,
),
val count: Int = 1,
)

Expand Down Expand Up @@ -116,3 +127,14 @@ public data class PowerModelSpec(
require(maxPower >= idlePower) { "The max power of a power model can not be less than the idle power" }
}
}

@Serializable
public data class ThermalModelSpec(
val modelType: String,
val rHS: Double,
val rCase: Double,
val minLeakageCurrent: Double,
val maxLeakageCurrent: Double,
val supplyVoltage: Double,
val ambientTemperature: Double,
)
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public final class SimBareMetalMachine extends SimAbstractMachine {
private final Memory memory;
private final List<NetworkAdapter> net;
private final List<StorageDevice> disk;

/**
* Construct a {@link SimBareMetalMachine} instance.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ public abstract class SimPsu extends SimPowerInlet {
*/
public abstract double getPowerDraw();

/**
* Return the temperature of the Host in degrees Celsius based on a power equation.
*/
public abstract double getCpuTemperature();

/**
* Set the temperature of the Host in degrees Celsius based on a power equation.
*/
public abstract void setTemperature();

/**
* Return the cumulated energy usage of the machine (in J) measured at the inlet of the powers supply.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.jetbrains.annotations.NotNull;
import org.opendc.simulator.compute.model.ProcessingUnit;
import org.opendc.simulator.compute.power.CpuPowerModel;
import org.opendc.simulator.compute.thermal.ThermalModel;
import org.opendc.simulator.flow2.FlowGraph;
import org.opendc.simulator.flow2.FlowStage;
import org.opendc.simulator.flow2.FlowStageLogic;
Expand Down Expand Up @@ -57,8 +58,8 @@ public static SimPsuFactory noop() {
*
* @param model The power model to estimate the power consumption based on the CPU usage.
*/
public static SimPsuFactory simple(CpuPowerModel model) {
return (machine, graph) -> new SimplePsu(graph, model);
public static SimPsuFactory simple(CpuPowerModel model, ThermalModel thermalModel) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we would make the thermalModel variable nullable.
Currently, you always have to put in a thermalModel.
This is not ideal.

return (machine, graph) -> new SimplePsu(graph, model, thermalModel);
}

/**
Expand Down Expand Up @@ -91,6 +92,14 @@ public double getEnergyUsage() {
return 0;
}

@Override
public double getCpuTemperature() {
return 0;
}

@Override
public void setTemperature() {}

@Override
InPort getCpuPower(int id, ProcessingUnit model) {
final InPort port = stage.getInlet("cpu" + id);
Expand All @@ -117,6 +126,7 @@ private static final class SimplePsu extends SimPsu implements FlowStageLogic {
private final FlowStage stage;
private final OutPort out;
private final CpuPowerModel model;
private final ThermalModel thermalModel;
private final InstantSource clock;

private double targetFreq;
Expand All @@ -125,6 +135,10 @@ private static final class SimplePsu extends SimPsu implements FlowStageLogic {

private double powerDraw;
private double energyUsage;
private double voltage = 1.8; // FIXME implement voltage when PSU is fully implemented
private double current; // FIXME implement current when PSU is fully implemented
private double staticPower;
private double machineTemp;

private final InHandler handler = new InHandler() {
@Override
Expand All @@ -138,10 +152,11 @@ public void onUpstreamFinish(InPort port, Throwable cause) {
}
};

SimplePsu(FlowGraph graph, CpuPowerModel model) {
SimplePsu(FlowGraph graph, CpuPowerModel model, ThermalModel thermalModel) {
this.stage = graph.newStage(this);
this.model = model;
this.clock = graph.getEngine().getClock();
this.thermalModel = thermalModel;
this.out = stage.getOutlet("out");
this.out.setMask(true);

Expand All @@ -158,6 +173,19 @@ public double getPowerDraw() {
return powerDraw;
}

@Override
public void setTemperature() {
double minPower = model.computePower(0.0);
double maxPower = model.computePower(1.0);

machineTemp = thermalModel.setTemperature(powerDraw, minPower, maxPower);
}

@Override
public double getCpuTemperature() {
return machineTemp;
}

@Override
public double getEnergyUsage() {
updateEnergyUsage(clock.millis());
Expand Down Expand Up @@ -188,6 +216,8 @@ public long onUpdate(FlowStage ctx, long now) {
out.push((float) usage);
powerDraw = usage;

setTemperature();

return Long.MAX_VALUE;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.opendc.simulator.compute.thermal;

public interface ThermalModel {

double setTemperature(double dynamicPower, double minPower, double maxPower);
}
Loading
Loading