Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
fix example (bummer)
[simgrid.git] / src / surf / plugins / host_energy.cpp
index 7a3029a..d64ed6f 100644 (file)
@@ -7,7 +7,13 @@
 #include "simgrid/simix.hpp"
 #include "src/plugins/vm/VirtualMachineImpl.hpp"
 #include "src/surf/cpu_interface.hpp"
 #include "simgrid/simix.hpp"
 #include "src/plugins/vm/VirtualMachineImpl.hpp"
 #include "src/surf/cpu_interface.hpp"
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <simgrid/s4u/engine.hpp>
+#include <string>
 #include <utility>
 #include <utility>
+#include <vector>
 
 /** @addtogroup SURF_plugin_energy
 
 
 /** @addtogroup SURF_plugin_energy
 
@@ -19,14 +25,14 @@ The energy consumption of a CPU depends directly of its current load. Specify th
 follows:
 
 \verbatim
 follows:
 
 \verbatim
-<host id="HostA" power="100.0Mf" >
-    <prop id="watt_per_state" value="100.0:200.0" />
+<host id="HostA" power="100.0Mf" cores="8">
+    <prop id="watt_per_state" value="100.0:120.0:200.0" />
     <prop id="watt_off" value="10" />
 </host>
 \endverbatim
 
 The first property means that when your host is up and running, but without anything to do, it will dissipate 100 Watts.
     <prop id="watt_off" value="10" />
 </host>
 \endverbatim
 
 The first property means that when your host is up and running, but without anything to do, it will dissipate 100 Watts.
-If it's fully loaded, it will dissipate 200 Watts. If its load is at 50%, then it will dissipate 150 Watts.
+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.
 The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these
 values are arbitrary).
 
 The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these
 values are arbitrary).
 
@@ -34,7 +40,7 @@ If your CPU is using pstates, then you can provide one consumption interval per
 
 \verbatim
 <host id="HostB" power="100.0Mf,50.0Mf,20.0Mf" pstate="0" >
 
 \verbatim
 <host id="HostB" power="100.0Mf,50.0Mf,20.0Mf" pstate="0" >
-    <prop id="watt_per_state" value="95.0:200.0, 93.0:170.0, 90.0:150.0" />
+    <prop id="watt_per_state" value="95.0:120.0:200.0, 93.0:115.0:170.0, 90.0:110.0:150.0" />
     <prop id="watt_off" value="10" />
 </host>
 \endverbatim
     <prop id="watt_off" value="10" />
 </host>
 \endverbatim
@@ -43,7 +49,7 @@ That host has 3 levels of performance with the following performance: 100 Mflop/
 It starts at pstate 0 (ie, at 100 Mflop/s). In this case, you have to specify one interval per pstate in the
 watt_per_state property.
 In this example, the idle consumption is 95 Watts, 93 Watts and 90 Watts in each pstate while the CPU burn consumption
 It starts at pstate 0 (ie, at 100 Mflop/s). In this case, you have to specify one interval per pstate in the
 watt_per_state property.
 In this example, the idle consumption is 95 Watts, 93 Watts and 90 Watts in each pstate while the CPU burn consumption
-are at 200 Watts, 170 Watts, and 150 Watts respectively.
+are at 200 Watts, 170 Watts, and 150 Watts respectively. If only one core is active, this machine consumes 120 / 115 / 110 watts.
 
 To change the pstate of a given CPU, use the following functions:
 #MSG_host_get_nb_pstates(), simgrid#s4u#Host#setPstate(), #MSG_host_get_power_peak_at().
 
 To change the pstate of a given CPU, use the following functions:
 #MSG_host_get_nb_pstates(), simgrid#s4u#Host#setPstate(), #MSG_host_get_power_peak_at().
@@ -57,8 +63,6 @@ XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf, "Logging specific to the SURF
 namespace simgrid {
 namespace energy {
 
 namespace simgrid {
 namespace energy {
 
-class XBT_PRIVATE HostEnergy;
-
 class PowerRange {
 public:
   double idle;
 class PowerRange {
 public:
   double idle;
@@ -86,6 +90,13 @@ private:
   simgrid::s4u::Host* host = nullptr;
   std::vector<PowerRange>
       power_range_watts_list; /*< List of (min_power,max_power) pairs corresponding to each cpu pstate */
   simgrid::s4u::Host* host = nullptr;
   std::vector<PowerRange>
       power_range_watts_list; /*< List of (min_power,max_power) pairs corresponding to each cpu pstate */
+
+  /* We need to keep track of what pstate has been used, as we will sometimes
+   * be notified only *after* a pstate has been used (but we need to update the energy consumption
+   * with the old pstate!)
+   */
+  int pstate = 0;
+
 public:
   double watts_off    = 0.0; /*< Consumption when the machine is turned off (shutdown) */
   double total_energy = 0.0; /*< Total energy consumed by the host */
 public:
   double watts_off    = 0.0; /*< Consumption when the machine is turned off (shutdown) */
   double total_energy = 0.0; /*< Total energy consumed by the host */
@@ -100,12 +111,13 @@ void HostEnergy::update()
   double start_time  = this->last_updated;
   double finish_time = surf_get_clock();
   double cpu_load;
   double start_time  = this->last_updated;
   double finish_time = surf_get_clock();
   double cpu_load;
-  if (host->pimpl_cpu->getPstateSpeedCurrent() <= 0)
+  double current_speed = host->speed();
+  if (current_speed <= 0)
     // Some users declare a pstate of speed 0 flops (e.g., to model boot time).
     // We consider that the machine is then fully loaded. That's arbitrary but it avoids a NaN
     cpu_load = 1;
   else
     // Some users declare a pstate of speed 0 flops (e.g., to model boot time).
     // We consider that the machine is then fully loaded. That's arbitrary but it avoids a NaN
     cpu_load = 1;
   else
-    cpu_load = lmm_constraint_get_usage(host->pimpl_cpu->constraint()) / host->pimpl_cpu->getPstateSpeedCurrent();
+    cpu_load = lmm_constraint_get_usage(host->pimpl_cpu->constraint()) / current_speed;
 
   /** Divide by the number of cores here **/
   cpu_load /= host->pimpl_cpu->coreCount();
 
   /** Divide by the number of cores here **/
   cpu_load /= host->pimpl_cpu->coreCount();
@@ -133,7 +145,7 @@ void HostEnergy::update()
 
   this->total_energy = previous_energy + energy_this_step;
   this->last_updated = finish_time;
 
   this->total_energy = previous_energy + energy_this_step;
   this->last_updated = finish_time;
-
+  this->pstate       = host->pstate();
   XBT_DEBUG(
       "[update_energy of %s] period=[%.2f-%.2f]; current power peak=%.0E flop/s; consumption change: %.2f J -> %.2f J",
       host->cname(), start_time, finish_time, host->pimpl_cpu->speed_.peak, previous_energy, energy_this_step);
   XBT_DEBUG(
       "[update_energy of %s] period=[%.2f-%.2f]; current power peak=%.0E flop/s; consumption change: %.2f J -> %.2f J",
       host->cname(), start_time, finish_time, host->pimpl_cpu->speed_.peak, previous_energy, energy_this_step);
@@ -171,9 +183,9 @@ double HostEnergy::getCurrentWattsValue(double cpu_load)
 {
   xbt_assert(!power_range_watts_list.empty(), "No power range properties specified for host %s", host->cname());
 
 {
   xbt_assert(!power_range_watts_list.empty(), "No power range properties specified for host %s", host->cname());
 
-  /* min_power corresponds to the idle power (cpu load = 0) */
+  /* min_power corresponds to the power consumed when only one core is active */
   /* max_power is the power consumed at 100% cpu load       */
   /* max_power is the power consumed at 100% cpu load       */
-  auto range           = power_range_watts_list.at(host->pstate());
+  auto range           = power_range_watts_list.at(this->pstate);
   double current_power = 0;
   double min_power     = 0;
   double max_power     = 0;
   double current_power = 0;
   double min_power     = 0;
   double max_power     = 0;
@@ -226,14 +238,16 @@ void HostEnergy::initWattsRangeList()
   if (all_power_values_str == nullptr)
     return;
 
   if (all_power_values_str == nullptr)
     return;
 
-  xbt_dynar_t all_power_values = xbt_str_split(all_power_values_str, ",");
-  int pstate_nb                = xbt_dynar_length(all_power_values);
+  std::vector<std::string> all_power_values;
+  boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
 
 
-  for (int i = 0; i < pstate_nb; i++) {
+  int i = 0;
+  for (auto current_power_values_str : all_power_values) {
     /* retrieve the power values associated with the current pstate */
     /* retrieve the power values associated with the current pstate */
-    xbt_dynar_t current_power_values = xbt_str_split(xbt_dynar_get_as(all_power_values, i, char*), ":");
-    xbt_assert(xbt_dynar_length(current_power_values) == 3,
-               "Power properties incorrectly defined - could not retrieve idle, min and max power values for host %s",
+    std::vector<std::string> current_power_values;
+    boost::split(current_power_values, current_power_values_str, boost::is_any_of(":"));
+    xbt_assert(current_power_values.size() == 3, "Power properties incorrectly defined - "
+                                                 "could not retrieve idle, min and max power values for host %s",
                host->cname());
 
     /* min_power corresponds to the idle power (cpu load = 0) */
                host->cname());
 
     /* min_power corresponds to the idle power (cpu load = 0) */
@@ -241,17 +255,15 @@ void HostEnergy::initWattsRangeList()
     char* msg_idle = bprintf("Invalid idle value for pstate %d on host %s: %%s", i, host->cname());
     char* msg_min  = bprintf("Invalid min value for pstate %d on host %s: %%s", i, host->cname());
     char* msg_max  = bprintf("Invalid max value for pstate %d on host %s: %%s", i, host->cname());
     char* msg_idle = bprintf("Invalid idle value for pstate %d on host %s: %%s", i, host->cname());
     char* msg_min  = bprintf("Invalid min value for pstate %d on host %s: %%s", i, host->cname());
     char* msg_max  = bprintf("Invalid max value for pstate %d on host %s: %%s", i, host->cname());
-    PowerRange range(xbt_str_parse_double(xbt_dynar_get_as(current_power_values, 0, char*), msg_idle),
-                     xbt_str_parse_double(xbt_dynar_get_as(current_power_values, 1, char*), msg_min),
-                     xbt_str_parse_double(xbt_dynar_get_as(current_power_values, 2, char*), msg_max));
+    PowerRange range(xbt_str_parse_double((current_power_values.at(0)).c_str(), msg_idle),
+                     xbt_str_parse_double((current_power_values.at(1)).c_str(), msg_min),
+                     xbt_str_parse_double((current_power_values.at(2)).c_str(), msg_max));
     power_range_watts_list.push_back(range);
     xbt_free(msg_idle);
     xbt_free(msg_min);
     xbt_free(msg_max);
     power_range_watts_list.push_back(range);
     xbt_free(msg_idle);
     xbt_free(msg_min);
     xbt_free(msg_max);
-
-    xbt_dynar_free(&current_power_values);
+    i++;
   }
   }
-  xbt_dynar_free(&all_power_values);
 }
 }
 }
 }
 }
 }
@@ -270,31 +282,32 @@ static void onActionStateChange(simgrid::surf::CpuAction* action, simgrid::surf:
 {
   for (simgrid::surf::Cpu* cpu : action->cpus()) {
     simgrid::s4u::Host* host = cpu->getHost();
 {
   for (simgrid::surf::Cpu* cpu : action->cpus()) {
     simgrid::s4u::Host* host = cpu->getHost();
-    if (host == nullptr)
-      continue;
+    if (host != nullptr) {
 
 
-    // If it's a VM, take the corresponding PM
-    simgrid::s4u::VirtualMachine* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host);
-    if (vm) // If it's a VM, take the corresponding PM
-      host = vm->pimpl_vm_->getPm();
+      // If it's a VM, take the corresponding PM
+      simgrid::s4u::VirtualMachine* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host);
+      if (vm) // If it's a VM, take the corresponding PM
+        host = vm->pimpl_vm_->getPm();
 
 
-    // Get the host_energy extension for the relevant host
-    HostEnergy* host_energy = host->extension<HostEnergy>();
+      // Get the host_energy extension for the relevant host
+      HostEnergy* host_energy = host->extension<HostEnergy>();
 
 
-    if (host_energy->last_updated < surf_get_clock())
-      host_energy->update();
+      if (host_energy->last_updated < surf_get_clock())
+        host_energy->update();
+    }
   }
 }
 
   }
 }
 
-static void onHostStateChange(simgrid::s4u::Host& host)
+/* This callback is fired either when the host changes its state (on/off) ("onStateChange") or its speed
+ * (because the user changed the pstate, or because of external trace events) ("onSpeedChange") */
+static void onHostChange(simgrid::s4u::Host& host)
 {
   if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
     return;
 
   HostEnergy* host_energy = host.extension<HostEnergy>();
 
 {
   if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
     return;
 
   HostEnergy* host_energy = host.extension<HostEnergy>();
 
-  if (host_energy->last_updated < surf_get_clock())
-    host_energy->update();
+  host_energy->update();
 }
 
 static void onHostDestruction(simgrid::s4u::Host& host)
 }
 
 static void onHostDestruction(simgrid::s4u::Host& host)
@@ -304,14 +317,36 @@ static void onHostDestruction(simgrid::s4u::Host& host)
 
   HostEnergy* host_energy = host.extension<HostEnergy>();
   host_energy->update();
 
   HostEnergy* host_energy = host.extension<HostEnergy>();
   host_energy->update();
-  XBT_INFO("Total energy of host %s: %f Joules", host.cname(), host_energy->getConsumedEnergy());
+  XBT_INFO("Energy consumption of host %s: %f Joules", host.cname(), host_energy->getConsumedEnergy());
+}
+
+static void onSimulationEnd()
+{
+  sg_host_t* host_list     = sg_host_list();
+  int host_count           = sg_host_count();
+  double total_energy      = 0.0; // Total energy consumption (whole platform)
+  double used_hosts_energy = 0.0; // Energy consumed by hosts that computed something
+  for (int i = 0; i < host_count; i++) {
+    if (dynamic_cast<simgrid::s4u::VirtualMachine*>(host_list[i]) == nullptr) { // Ignore virtual machines
+
+      bool host_was_used = (host_list[i]->extension<HostEnergy>()->last_updated != 0);
+      double energy      = host_list[i]->extension<HostEnergy>()->getConsumedEnergy();
+      total_energy      += energy;
+      if (host_was_used)
+        used_hosts_energy += energy;
+    }
+  }
+  XBT_INFO("Total energy consumption: %f Joules (used hosts: %f Joules; unused/idle hosts: %f)",
+           total_energy, used_hosts_energy, total_energy - used_hosts_energy);
+  xbt_free(host_list);
 }
 
 /* **************************** Public interface *************************** */
 }
 
 /* **************************** Public interface *************************** */
+SG_BEGIN_DECL()
+
 /** \ingroup SURF_plugin_energy
 /** \ingroup SURF_plugin_energy
- * \brief Enable energy plugin
- * \details Enable energy plugin to get joules consumption of each cpu. You should call this function before
- * #MSG_init().
+ * \brief Enable host energy plugin
+ * \details Enable energy plugin to get joules consumption of each cpu. Call this function before #MSG_init().
  */
 void sg_host_energy_plugin_init()
 {
  */
 void sg_host_energy_plugin_init()
 {
@@ -321,8 +356,10 @@ void sg_host_energy_plugin_init()
   HostEnergy::EXTENSION_ID = simgrid::s4u::Host::extension_create<HostEnergy>();
 
   simgrid::s4u::Host::onCreation.connect(&onCreation);
   HostEnergy::EXTENSION_ID = simgrid::s4u::Host::extension_create<HostEnergy>();
 
   simgrid::s4u::Host::onCreation.connect(&onCreation);
-  simgrid::s4u::Host::onStateChange.connect(&onHostStateChange);
+  simgrid::s4u::Host::onStateChange.connect(&onHostChange);
+  simgrid::s4u::Host::onSpeedChange.connect(&onHostChange);
   simgrid::s4u::Host::onDestruction.connect(&onHostDestruction);
   simgrid::s4u::Host::onDestruction.connect(&onHostDestruction);
+  simgrid::s4u::onSimulationEnd.connect(&onSimulationEnd);
   simgrid::surf::CpuAction::onStateChange.connect(&onActionStateChange);
 }
 
   simgrid::surf::CpuAction::onStateChange.connect(&onActionStateChange);
 }
 
@@ -351,3 +388,5 @@ double sg_host_get_wattmax_at(sg_host_t host, int pstate)
              "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
   return host->extension<HostEnergy>()->getWattMaxAt(pstate);
 }
              "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
   return host->extension<HostEnergy>()->getWattMaxAt(pstate);
 }
+
+SG_END_DECL()