1 /* Copyright (c) 2010, 2012-2016. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 #include "simgrid/plugins/energy.h"
7 #include "simgrid/simix.hpp"
8 #include "src/plugins/vm/VirtualMachineImpl.hpp"
9 #include "src/surf/cpu_interface.hpp"
11 #include <boost/algorithm/string/classification.hpp>
12 #include <boost/algorithm/string/split.hpp>
13 #include <simgrid/s4u/engine.hpp>
18 /** @addtogroup SURF_plugin_energy
21 This is the energy plugin, enabling to account not only for computation time,
22 but also for the dissipated energy in the simulated platform.
24 The energy consumption of a CPU depends directly of its current load. Specify that consumption in your platform file as
28 <host id="HostA" power="100.0Mf" cores="8">
29 <prop id="watt_per_state" value="100.0:120.0:200.0" />
30 <prop id="watt_off" value="10" />
34 The first property means that when your host is up and running, but without anything to do, it will dissipate 100 Watts.
35 If only one care is active, it will dissipate 120 Watts. If it's fully loaded, it will dissipate 200 Watts. If its load is at 50%, then it will dissipate 153.33 Watts.
36 The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these
37 values are arbitrary).
39 If your CPU is using pstates, then you can provide one consumption interval per pstate.
42 <host id="HostB" power="100.0Mf,50.0Mf,20.0Mf" pstate="0" >
43 <prop id="watt_per_state" value="95.0:120.0:200.0, 93.0:115.0:170.0, 90.0:110.0:150.0" />
44 <prop id="watt_off" value="10" />
48 That host has 3 levels of performance with the following performance: 100 Mflop/s, 50 Mflop/s or 20 Mflop/s.
49 It starts at pstate 0 (ie, at 100 Mflop/s). In this case, you have to specify one interval per pstate in the
50 watt_per_state property.
51 In this example, the idle consumption is 95 Watts, 93 Watts and 90 Watts in each pstate while the CPU burn consumption
52 are at 200 Watts, 170 Watts, and 150 Watts respectively. If only one core is active, this machine consumes 120 / 115 / 110 watts.
54 To change the pstate of a given CPU, use the following functions:
55 #MSG_host_get_nb_pstates(), simgrid#s4u#Host#setPstate(), #MSG_host_get_power_peak_at().
57 To simulate the energy-related elements, first call the simgrid#energy#sg_energy_plugin_init() before your #MSG_init(),
58 and then use the following function to retrieve the consumption of a given host: MSG_host_get_consumed_energy().
61 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf, "Logging specific to the SURF energy plugin");
72 PowerRange(double idle, double min, double max) : idle(idle), min(min), max(max) {}
77 static simgrid::xbt::Extension<simgrid::s4u::Host, HostEnergy> EXTENSION_ID;
79 explicit HostEnergy(simgrid::s4u::Host* ptr);
82 double getCurrentWattsValue(double cpu_load);
83 double getConsumedEnergy();
84 double getWattMinAt(int pstate);
85 double getWattMaxAt(int pstate);
89 void initWattsRangeList();
90 simgrid::s4u::Host* host = nullptr;
91 std::vector<PowerRange>
92 power_range_watts_list; /*< List of (min_power,max_power) pairs corresponding to each cpu pstate */
94 double watts_off = 0.0; /*< Consumption when the machine is turned off (shutdown) */
95 double total_energy = 0.0; /*< Total energy consumed by the host */
96 double last_updated; /*< Timestamp of the last energy update event*/
99 simgrid::xbt::Extension<simgrid::s4u::Host, HostEnergy> HostEnergy::EXTENSION_ID;
101 /* Computes the consumption so far. Called lazily on need. */
102 void HostEnergy::update()
104 double start_time = this->last_updated;
105 double finish_time = surf_get_clock();
107 double current_speed = host->pimpl_cpu->getPstateSpeedCurrent();
108 if (current_speed <= 0)
109 // Some users declare a pstate of speed 0 flops (e.g., to model boot time).
110 // We consider that the machine is then fully loaded. That's arbitrary but it avoids a NaN
113 cpu_load = lmm_constraint_get_usage(host->pimpl_cpu->constraint()) / current_speed;
115 /** Divide by the number of cores here **/
116 cpu_load /= host->pimpl_cpu->coreCount();
118 if (cpu_load > 1) // A machine with a load > 1 consumes as much as a fully loaded machine, not more
121 /* The problem with this model is that the load is always 0 or 1, never something less.
122 * Another possibility could be to model the total energy as
124 * X/(X+Y)*W_idle + Y/(X+Y)*W_burn
126 * where X is the amount of idling cores, and Y the amount of computing cores.
129 double previous_energy = this->total_energy;
131 double instantaneous_consumption;
133 instantaneous_consumption = this->watts_off;
135 instantaneous_consumption = this->getCurrentWattsValue(cpu_load);
137 double energy_this_step = instantaneous_consumption * (finish_time - start_time);
139 this->total_energy = previous_energy + energy_this_step;
140 this->last_updated = finish_time;
143 "[update_energy of %s] period=[%.2f-%.2f]; current power peak=%.0E flop/s; consumption change: %.2f J -> %.2f J",
144 host->cname(), start_time, finish_time, host->pimpl_cpu->speed_.peak, previous_energy, energy_this_step);
147 HostEnergy::HostEnergy(simgrid::s4u::Host* ptr) : host(ptr), last_updated(surf_get_clock())
149 initWattsRangeList();
151 const char* off_power_str = host->property("watt_off");
152 if (off_power_str != nullptr) {
153 char* msg = bprintf("Invalid value for property watt_off of host %s: %%s", host->cname());
154 this->watts_off = xbt_str_parse_double(off_power_str, msg);
157 /* watts_off is 0 by default */
160 HostEnergy::~HostEnergy() = default;
162 double HostEnergy::getWattMinAt(int pstate)
164 xbt_assert(!power_range_watts_list.empty(), "No power range properties specified for host %s", host->cname());
165 return power_range_watts_list[pstate].min;
168 double HostEnergy::getWattMaxAt(int pstate)
170 xbt_assert(!power_range_watts_list.empty(), "No power range properties specified for host %s", host->cname());
171 return power_range_watts_list[pstate].max;
174 /** @brief Computes the power consumed by the host according to the current pstate and processor load */
175 double HostEnergy::getCurrentWattsValue(double cpu_load)
177 xbt_assert(!power_range_watts_list.empty(), "No power range properties specified for host %s", host->cname());
179 /* min_power corresponds to the power consumed when only one core is active */
180 /* max_power is the power consumed at 100% cpu load */
181 auto range = power_range_watts_list.at(host->pstate());
182 double current_power = 0;
183 double min_power = 0;
184 double max_power = 0;
185 double power_slope = 0;
187 if (cpu_load > 0) { /* Something is going on, the machine is not idle */
188 double min_power = range.min;
189 double max_power = range.max;
192 * The min_power states how much we consume when only one single
193 * core is working. This means that when cpu_load == 1/coreCount, then
194 * current_power == min_power.
196 * The maximum must be reached when all cores are working (but 1 core was
197 * already accounted for by min_power)
198 * i.e., we need min_power + (maxCpuLoad-1/coreCount)*power_slope == max_power
199 * (maxCpuLoad is by definition 1)
202 int coreCount = host->coreCount();
203 double coreReciprocal = static_cast<double>(1) / static_cast<double>(coreCount);
205 power_slope = (max_power - min_power) / (1 - coreReciprocal);
207 power_slope = 0; // Should be 0, since max_power == min_power (in this case)
209 current_power = min_power + (cpu_load - coreReciprocal) * power_slope;
210 } else { /* Our machine is idle, take the dedicated value! */
211 current_power = range.idle;
214 XBT_DEBUG("[get_current_watts] min_power=%f, max_power=%f, slope=%f", min_power, max_power, power_slope);
215 XBT_DEBUG("[get_current_watts] Current power (watts) = %f, load = %f", current_power, cpu_load);
217 return current_power;
220 double HostEnergy::getConsumedEnergy()
222 if (last_updated < surf_get_clock()) // We need to simcall this as it modifies the environment
223 simgrid::simix::kernelImmediate(std::bind(&HostEnergy::update, this));
228 void HostEnergy::initWattsRangeList()
230 const char* all_power_values_str = host->property("watt_per_state");
231 if (all_power_values_str == nullptr)
234 std::vector<std::string> all_power_values;
235 boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
238 for (auto current_power_values_str : all_power_values) {
239 /* retrieve the power values associated with the current pstate */
240 std::vector<std::string> current_power_values;
241 boost::split(current_power_values, current_power_values_str, boost::is_any_of(":"));
242 xbt_assert(current_power_values.size() == 3, "Power properties incorrectly defined - "
243 "could not retrieve idle, min and max power values for host %s",
246 /* min_power corresponds to the idle power (cpu load = 0) */
247 /* max_power is the power consumed at 100% cpu load */
248 char* msg_idle = bprintf("Invalid idle value for pstate %d on host %s: %%s", i, host->cname());
249 char* msg_min = bprintf("Invalid min value for pstate %d on host %s: %%s", i, host->cname());
250 char* msg_max = bprintf("Invalid max value for pstate %d on host %s: %%s", i, host->cname());
251 PowerRange range(xbt_str_parse_double((current_power_values.at(0)).c_str(), msg_idle),
252 xbt_str_parse_double((current_power_values.at(1)).c_str(), msg_min),
253 xbt_str_parse_double((current_power_values.at(2)).c_str(), msg_max));
254 power_range_watts_list.push_back(range);
264 using simgrid::energy::HostEnergy;
266 /* **************************** events callback *************************** */
267 static void onCreation(simgrid::s4u::Host& host)
269 if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
271 host.extension_set(new HostEnergy(&host));
274 static void onActionStateChange(simgrid::surf::CpuAction* action, simgrid::surf::Action::State previous)
276 for (simgrid::surf::Cpu* cpu : action->cpus()) {
277 simgrid::s4u::Host* host = cpu->getHost();
281 // If it's a VM, take the corresponding PM
282 simgrid::s4u::VirtualMachine* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host);
283 if (vm) // If it's a VM, take the corresponding PM
284 host = vm->pimpl_vm_->getPm();
286 // Get the host_energy extension for the relevant host
287 HostEnergy* host_energy = host->extension<HostEnergy>();
289 if (host_energy->last_updated < surf_get_clock())
290 host_energy->update();
294 /* This callback is fired either when the host change its state (on/off) or its speed
295 * (because the user changed the pstate, or because of external trace events) */
296 static void onHostChange(simgrid::s4u::Host& host)
298 if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
301 HostEnergy* host_energy = host.extension<HostEnergy>();
303 if (host_energy->last_updated < surf_get_clock())
304 host_energy->update();
307 static void onHostDestruction(simgrid::s4u::Host& host)
309 if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
312 HostEnergy* host_energy = host.extension<HostEnergy>();
313 host_energy->update();
314 XBT_INFO("Energy consumption of host %s: %f Joules", host.cname(), host_energy->getConsumedEnergy());
317 static void onSimulationEnd()
319 sg_host_t* host_list = sg_host_list();
320 int host_count = sg_host_count();
321 double total_energy = 0.0; // Total energy consumption (whole platform)
322 double used_hosts_energy = 0.0; // Energy consumed by hosts that computed something
323 for (int i = 0; i < host_count; i++) {
324 if (dynamic_cast<simgrid::s4u::VirtualMachine*>(host_list[i]) == nullptr) { // Ignore virtual machines
326 bool host_was_used = (host_list[i]->extension<HostEnergy>()->last_updated != 0);
327 double energy = host_list[i]->extension<HostEnergy>()->getConsumedEnergy();
328 total_energy += energy;
330 used_hosts_energy += energy;
333 XBT_INFO("Total energy consumption: %f Joules (used hosts: %f Joules; unused/idle hosts: %f)",
334 total_energy, used_hosts_energy, total_energy - used_hosts_energy);
338 /* **************************** Public interface *************************** */
341 /** \ingroup SURF_plugin_energy
342 * \brief Enable host energy plugin
343 * \details Enable energy plugin to get joules consumption of each cpu. Call this function before #MSG_init().
345 void sg_host_energy_plugin_init()
347 if (HostEnergy::EXTENSION_ID.valid())
350 HostEnergy::EXTENSION_ID = simgrid::s4u::Host::extension_create<HostEnergy>();
352 simgrid::s4u::Host::onCreation.connect(&onCreation);
353 simgrid::s4u::Host::onStateChange.connect(&onHostChange);
354 simgrid::s4u::Host::onSpeedChange.connect(&onHostChange);
355 simgrid::s4u::Host::onDestruction.connect(&onHostDestruction);
356 simgrid::s4u::onSimulationEnd.connect(&onSimulationEnd);
357 simgrid::surf::CpuAction::onStateChange.connect(&onActionStateChange);
360 /** @brief Returns the total energy consumed by the host so far (in Joules)
362 * See also @ref SURF_plugin_energy.
364 double sg_host_get_consumed_energy(sg_host_t host)
366 xbt_assert(HostEnergy::EXTENSION_ID.valid(),
367 "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
368 return host->extension<HostEnergy>()->getConsumedEnergy();
371 /** @brief Get the amount of watt dissipated at the given pstate when the host is idling */
372 double sg_host_get_wattmin_at(sg_host_t host, int pstate)
374 xbt_assert(HostEnergy::EXTENSION_ID.valid(),
375 "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
376 return host->extension<HostEnergy>()->getWattMinAt(pstate);
378 /** @brief Returns the amount of watt dissipated at the given pstate when the host burns CPU at 100% */
379 double sg_host_get_wattmax_at(sg_host_t host, int pstate)
381 xbt_assert(HostEnergy::EXTENSION_ID.valid(),
382 "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
383 return host->extension<HostEnergy>()->getWattMaxAt(pstate);