Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
separate the energy plugin from surf in the doc + improvements
[simgrid.git] / src / surf / plugins / host_energy.cpp
index 9698183..42efb75 100644 (file)
 
 #include "simgrid/s4u/Engine.hpp"
 
 
 #include "simgrid/s4u/Engine.hpp"
 
-#include <algorithm>
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <string>
 #include <utility>
 #include <vector>
 
-/** @addtogroup SURF_plugin_energy
+/** @addtogroup plugin_energy
 
 
 This is the energy plugin, enabling to account not only for computation time,
 but also for the dissipated energy in the simulated platform.
 
 
 This is the energy plugin, enabling to account not only for computation time,
 but also for the dissipated energy in the simulated platform.
+To activate this plugin, first call sg_host_energy_plugin_init() before your #MSG_init(),
+and then use MSG_host_get_consumed_energy() to retrieve the consumption of a given host.
 
 
-The energy consumption of a CPU depends directly of its current load. Specify that consumption in your platform file as
-follows:
+When the host is on, this energy consumption naturally depends on both the
+current CPU load and the host energy profile. According to our measurements,
+the consumption is somehow linear in the amount of cores at full speed,
+with an abnormality when all the cores are idle. The full details are in
+<a href="https://hal.inria.fr/hal-01523608">our scientific paper</a> on that topic.
 
 
-\verbatim
-<host id="HostA" power="100.0Mf" cores="8">
+As a result, our energy model takes 4 parameters:
+
+  - \b Idle: instantaneous consumption (in Watt) when your host is up and running, but without anything to do.
+  - \b OneCore: instantaneous consumption (in Watt) when only one core is active, at 100%.
+  - \b AllCores: instantaneous consumption (in Watt) when all cores of the host are at 100%.
+  - \b Off: instantaneous consumption (in Watt) when the host is turned off.
+
+Here is an example of XML declaration:
+
+\code{.xml}
+<host id="HostA" power="100.0Mf" cores="4">
     <prop id="watt_per_state" value="100.0:120.0:200.0" />
     <prop id="watt_off" value="10" />
 </host>
     <prop id="watt_per_state" value="100.0:120.0:200.0" />
     <prop id="watt_off" value="10" />
 </host>
-\endverbatim
+\endcode
+
+This example gives the following parameters: \b Off is 10 Watts; \b Idle is 100 Watts; \b OneCore is 120 Watts and \b
+AllCores is 200 Watts.
+This is enough to compute the consumption as a function of the amount of loaded cores:
+
+<table>
+<tr><th>#Cores loaded</th><th>Consumption</th><th>Explanation</th></tr>
+<tr><td>0</td><td> 100 Watts</td><td>Idle value</td></tr>
+<tr><td>1</td><td> 120 Watts</td><td>OneCore value</td></tr>
+<tr><td>2</td><td> 147 Watts</td><td>linear extrapolation between OneCore and AllCores</td></tr>
+<tr><td>3</td><td> 173 Watts</td><td>linear extrapolation between OneCore and AllCores</td></tr>
+<tr><td>4</td><td> 200 Watts</td><td>AllCores value</td></tr>
+</table>
+
+### What if a given core is only at load 50%?
+
+This is impossible in SimGrid because we recompute everything each time
+that the CPU starts or stops doing something. So if a core is at load 50% over
+a period, it means that it is at load 100% half of the time and at load 0% the
+rest of the time, and our model holds.
+
+### What if the host has only one core?
+
+In this case, the parameters \b OneCore and \b AllCores are obviously the same.
+Actually, SimGrid expect an energetic profile formated as 'Idle:Running' for mono-cores hosts.
+If you insist on passing 3 parameters in this case, then you must have the same value for \b OneCore and \b AllCores.
+
+\code{.xml}
+<host id="HostC" power="100.0Mf" cores="1">
+    <prop id="watt_per_state" value="95.0:200.0" /> <!-- we may have used '95:200:200' instead -->
+    <prop id="watt_off" value="10" />
+</host>
+\endcode
 
 
-The first property means that when your host is up and running, but without anything to do, it will dissipate 100 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).
+### How does DVFS interact with the host energy model?
 
 
-If your CPU is using pstates, then you can provide one consumption interval per pstate.
+If your host has several DVFS levels (several pstates), then you should
+give the energetic profile of each pstate level:
 
 
-\verbatim
-<host id="HostB" power="100.0Mf,50.0Mf,20.0Mf" pstate="0" >
+\code{.xml}
+<host id="HostC" power="100.0Mf,50.0Mf,20.0Mf" cores="4">
     <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>
     <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
+\endcode
 
 
-That host has 3 levels of performance with the following performance: 100 Mflop/s, 50 Mflop/s or 20 Mflop/s.
-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. If only one core is active, this machine consumes 120 / 115 / 110 watts.
+This encodes the following values
+<table>
+<tr><th>pstate</th><th>Performance</th><th>Idle</th><th>OneCore</th><th>AllCores</th></tr>
+<tr><td>0</td><td>100 Mflop/s</td><td>95 Watts</td><td>120 Watts</td><td>200 Watts</td></tr>
+<tr><td>1</td><td>50 Mflop/s</td><td>93 Watts</td><td>115 Watts</td><td>170 Watts</td></tr>
+<tr><td>2</td><td>20 Mflop/s</td><td>90 Watts</td><td>110 Watts</td><td>150 Watts</td></tr>
+</table>
 
 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().
 
-To simulate the energy-related elements, first call the simgrid#energy#sg_energy_plugin_init() before your #MSG_init(),
-and then use the following function to retrieve the consumption of a given host: MSG_host_get_consumed_energy().
  */
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf, "Logging specific to the SURF energy plugin");
  */
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf, "Logging specific to the SURF energy plugin");
@@ -178,13 +222,6 @@ HostEnergy::HostEnergy(simgrid::s4u::Host* ptr) : host(ptr), last_updated(surf_g
     xbt_free(msg);
   }
   /* watts_off is 0 by default */
     xbt_free(msg);
   }
   /* watts_off is 0 by default */
-
-  if (ptr->coreCount() == 1)
-    xbt_assert(std::all_of(power_range_watts_list.begin(), power_range_watts_list.end(),
-                           [](PowerRange power_range) { return power_range.min == power_range.max; }),
-               "You only have one core in host %s, but the \
-      energy consumption for one core does not match the energy consumption for all (here: 1) cores). This is an error in your platform, please fix it.",
-               host->cname());
 }
 
 HostEnergy::~HostEnergy() = default;
 }
 
 HostEnergy::~HostEnergy() = default;
@@ -263,21 +300,41 @@ void HostEnergy::initWattsRangeList()
 
   std::vector<std::string> all_power_values;
   boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
 
   std::vector<std::string> all_power_values;
   boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
+  XBT_DEBUG("%s: profile: %s, cores: %d", host->cname(), all_power_values_str, host->coreCount());
 
   int i = 0;
   for (auto current_power_values_str : all_power_values) {
     /* retrieve the power values associated with the current pstate */
     std::vector<std::string> current_power_values;
     boost::split(current_power_values, current_power_values_str, boost::is_any_of(":"));
 
   int i = 0;
   for (auto current_power_values_str : all_power_values) {
     /* retrieve the power values associated with the current pstate */
     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());
+    if (host->coreCount() == 1) {
+      xbt_assert(current_power_values.size() == 2 || current_power_values.size() == 3,
+                 "Power properties incorrectly defined for host %s."
+                 "It should be 'Idle:FullSpeed' power values because you have one core only.",
+                 host->cname());
+      if (current_power_values.size() == 2) {
+        // In this case, 1core == AllCores
+        current_power_values.push_back(current_power_values.at(1));
+      } else { // size == 3
+        xbt_assert((current_power_values.at(1)) == (current_power_values.at(2)),
+                   "Power properties incorrectly defined for host %s.\n"
+                   "The energy profile of mono-cores should be formated as 'Idle:FullSpeed' only.\n"
+                   "If you go for a 'Idle:OneCore:AllCores' power profile on mono-cores, then OneCore and AllCores "
+                   "must be equal.",
+                   host->cname());
+      }
+    } else {
+      xbt_assert(current_power_values.size() == 3,
+                 "Power properties incorrectly defined for host %s."
+                 "It should be 'Idle:OneCore:AllCores' power values because you have more than one core.",
+                 host->cname());
+    }
 
     /* min_power corresponds to the idle power (cpu load = 0) */
     /* max_power is the power consumed at 100% cpu load       */
     char* msg_idle = bprintf("Invalid idle value for pstate %d on host %s: %%s", i, host->cname());
 
     /* min_power corresponds to the idle power (cpu load = 0) */
     /* max_power is the power consumed at 100% cpu load       */
     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_min  = bprintf("Invalid OneCore value for pstate %d on host %s: %%s", i, host->cname());
+    char* msg_max  = bprintf("Invalid AllCores value for pstate %d on host %s: %%s", i, host->cname());
     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));
     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));
@@ -370,7 +427,7 @@ static void onSimulationEnd()
 /* **************************** Public interface *************************** */
 SG_BEGIN_DECL()
 
 /* **************************** Public interface *************************** */
 SG_BEGIN_DECL()
 
-/** \ingroup SURF_plugin_energy
+/** \ingroup plugin_energy
  * \brief Enable host energy plugin
  * \details Enable energy plugin to get joules consumption of each cpu. 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().
  */
@@ -389,7 +446,8 @@ void sg_host_energy_plugin_init()
   simgrid::surf::CpuAction::onStateChange.connect(&onActionStateChange);
 }
 
   simgrid::surf::CpuAction::onStateChange.connect(&onActionStateChange);
 }
 
-/** @brief updates the consumption of all hosts
+/** @ingroup plugin_energy
+ *  @brief updates the consumption of all hosts
  *
  * After this call, sg_host_get_consumed_energy() will not interrupt your process
  * (until after the next clock update).
  *
  * After this call, sg_host_get_consumed_energy() will not interrupt your process
  * (until after the next clock update).
@@ -404,13 +462,12 @@ void sg_host_energy_update_all()
   });
 }
 
   });
 }
 
-/** @brief Returns the total energy consumed by the host so far (in Joules)
+/** @ingroup plugin_energy
+ *  @brief Returns the total energy consumed by the host so far (in Joules)
  *
  *  Please note that since the consumption is lazily updated, it may require a simcall to update it.
  *  The result is that the actor requesting this value will be interrupted,
  *  the value will be updated in kernel mode before returning the control to the requesting actor.
  *
  *  Please note that since the consumption is lazily updated, it may require a simcall to update it.
  *  The result is that the actor requesting this value will be interrupted,
  *  the value will be updated in kernel mode before returning the control to the requesting actor.
- *
- *  See also @ref SURF_plugin_energy.
  */
 double sg_host_get_consumed_energy(sg_host_t host)
 {
  */
 double sg_host_get_consumed_energy(sg_host_t host)
 {
@@ -419,14 +476,18 @@ double sg_host_get_consumed_energy(sg_host_t host)
   return host->extension<HostEnergy>()->getConsumedEnergy();
 }
 
   return host->extension<HostEnergy>()->getConsumedEnergy();
 }
 
-/** @brief Get the amount of watt dissipated at the given pstate when the host is idling */
+/** @ingroup plugin_energy
+ *  @brief Get the amount of watt dissipated at the given pstate when the host is idling
+ */
 double sg_host_get_wattmin_at(sg_host_t host, int pstate)
 {
   xbt_assert(HostEnergy::EXTENSION_ID.valid(),
              "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
   return host->extension<HostEnergy>()->getWattMinAt(pstate);
 }
 double sg_host_get_wattmin_at(sg_host_t host, int pstate)
 {
   xbt_assert(HostEnergy::EXTENSION_ID.valid(),
              "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
   return host->extension<HostEnergy>()->getWattMinAt(pstate);
 }
-/** @brief  Returns the amount of watt dissipated at the given pstate when the host burns CPU at 100% */
+/** @ingroup plugin_energy
+ *  @brief  Returns the amount of watt dissipated at the given pstate when the host burns CPU at 100%
+ */
 double sg_host_get_wattmax_at(sg_host_t host, int pstate)
 {
   xbt_assert(HostEnergy::EXTENSION_ID.valid(),
 double sg_host_get_wattmax_at(sg_host_t host, int pstate)
 {
   xbt_assert(HostEnergy::EXTENSION_ID.valid(),
@@ -434,7 +495,9 @@ double sg_host_get_wattmax_at(sg_host_t host, int pstate)
   return host->extension<HostEnergy>()->getWattMaxAt(pstate);
 }
 
   return host->extension<HostEnergy>()->getWattMaxAt(pstate);
 }
 
-/** @brief Returns the current consumption of the host */
+/** @ingroup plugin_energy
+ *  @brief Returns the current consumption of the host
+ */
 double sg_host_get_current_consumption(sg_host_t host)
 {
   xbt_assert(HostEnergy::EXTENSION_ID.valid(),
 double sg_host_get_current_consumption(sg_host_t host)
 {
   xbt_assert(HostEnergy::EXTENSION_ID.valid(),