Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Please Sonar and scan-build.
[simgrid.git] / src / plugins / host_energy.cpp
index 7917c2d..d2f5dc8 100644 (file)
@@ -1,16 +1,19 @@
-/* Copyright (c) 2010-2021. The SimGrid Team. All rights reserved.          */
+/* Copyright (c) 2010-2023. The SimGrid Team. All rights reserved.          */
 
 /* This program is free software; you can redistribute it and/or modify it
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
-#include "simgrid/Exception.hpp"
-#include "simgrid/plugins/energy.h"
-#include "simgrid/s4u/Engine.hpp"
-#include "simgrid/s4u/Exec.hpp"
-#include "src/include/surf/surf.hpp"
-#include "src/kernel/activity/ExecImpl.hpp"
-#include "src/plugins/vm/VirtualMachineImpl.hpp"
-#include "src/surf/cpu_interface.hpp"
+#include <simgrid/Exception.hpp>
+#include <simgrid/plugins/energy.h>
+#include <simgrid/s4u/Engine.hpp>
+#include <simgrid/s4u/Exec.hpp>
+#include <simgrid/s4u/Host.hpp>
+#include <simgrid/s4u/VirtualMachine.hpp>
+#include <simgrid/simix.hpp>
+
+#include "src/kernel/activity/ActivityImpl.hpp"
+#include "src/kernel/resource/CpuImpl.hpp"
+#include "src/simgrid/module.hpp"
 
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/split.hpp>
@@ -58,14 +61,25 @@ This is enough to compute the wattage as a function of the amount of loaded core
 
    <table border="1">
    <tr><th>#Cores loaded</th><th>Wattage</th><th>Explanation</th></tr>
-   <tr><td>0 (idle)</td><td> 100 Watts&nbsp;</td><td> Idle value</td></tr>
-   <tr><td>0 (not idle)</td><td> 120 Watts</td><td> Epsilon value</td></tr>
+   <tr><td>0 (idle)</td><td> 100 Watts&nbsp;</td><td>Idle value</td></tr>
    <tr><td>1</td><td> 140 Watts</td><td> Linear extrapolation between Epsilon and AllCores</td></tr>
    <tr><td>2</td><td> 160 Watts</td><td> Linear extrapolation between Epsilon and AllCores</td></tr>
    <tr><td>3</td><td> 180 Watts</td><td> Linear extrapolation between Epsilon and AllCores</td></tr>
    <tr><td>4</td><td> 200 Watts</td><td> AllCores value</td></tr>
    </table>
 
+Here is how it looks graphically:
+
+.. image:: img/plugin-energy.svg
+   :scale: 80%
+   :align: center
+
+As you can see, the ``Epsilon`` parameter allows to freely specify the slope you want, while using the 2 parameters
+version of the model (with only ``Idle`` and ``AllCores``) requires that the ``Idle`` value is on the extension of the
+line crossing the consumption you mesure for each core amount. Please note that specifying the consumption for each core
+amount separately was not a solution because parallel tasks can use an amount of cores that is not an integer. The good
+news is that it was not necessary, as our experiments (detailed in the paper) show that the proposed linear model is
+sufficient to capture reality.
 
 .. raw:: html
 
@@ -93,8 +107,8 @@ This encodes the following values:
    </table>
 
 To change the pstate of a given CPU, use the following functions:
-:cpp:func:`MSG_host_get_nb_pstates()`, :cpp:func:`simgrid::s4u::Host::set_pstate()`,
-:cpp:func:`MSG_host_get_power_peak_at()`.
+:cpp:func:`sg_host_get_nb_pstates()`, :cpp:func:`simgrid::s4u::Host::set_pstate()`,
+:cpp:func:`sg_host_get_pstate_speed()`.
 
 .. raw:: html
 
@@ -109,13 +123,12 @@ before you can get accurate energy predictions.
   @endrst
  */
 
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf, "Logging specific to the SURF energy plugin");
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(host_energy, kernel, "Logging specific to the host energy plugin");
 
 // Forwards declaration needed to make this function a friend (because friends have external linkage by default)
 static void on_simulation_end();
 
-namespace simgrid {
-namespace plugin {
+namespace simgrid::plugin {
 
 class PowerRange {
 public:
@@ -140,7 +153,7 @@ class HostEnergy {
   const int pstate_off_ = -1;
   double watts_off_     = 0.0;              /*< Consumption when the machine is turned off (shutdown) */
   double total_energy_  = 0.0;              /*< Total energy consumed by the host */
-  double last_updated_  = surf_get_clock(); /*< Timestamp of the last energy update event*/
+  double last_updated_  = simgrid::s4u::Engine::get_clock(); /*< Timestamp of the last energy update event*/
 
   /* Only used to split total energy into unused/used hosts.
    * If you want to get this info for something else, rather use the host_load plugin
@@ -180,7 +193,7 @@ bool HostEnergy::has_pstate_power_values() const {
 void HostEnergy::update()
 {
   double start_time  = last_updated_;
-  double finish_time = surf_get_clock();
+  double finish_time = simgrid::s4u::Engine::get_clock();
   //
   // We may have start == finish if the past consumption was updated since the simcall was started
   // for example if 2 actors requested to update the same host's consumption in a given scheduling round.
@@ -216,10 +229,10 @@ HostEnergy::HostEnergy(simgrid::s4u::Host* ptr) : host_(ptr)
   const char* off_power_str = host_->get_property("wattage_off");
   if (off_power_str != nullptr) {
     try {
-      this->watts_off_ = std::stod(std::string(off_power_str));
+      this->watts_off_ = std::stod(off_power_str);
     } catch (const std::invalid_argument&) {
-      throw std::invalid_argument(std::string("Invalid value for property wattage_off of host ") + host_->get_cname() +
-                                  ": " + off_power_str);
+      throw std::invalid_argument("Invalid value for property wattage_off of host " + host_->get_name() + ": " +
+                                  off_power_str);
     }
   }
   /* watts_off is 0 by default */
@@ -331,8 +344,8 @@ double HostEnergy::get_current_watts_value(double cpu_load) const
 
 double HostEnergy::get_consumed_energy()
 {
-  if (last_updated_ < surf_get_clock()) // We need to simcall this as it modifies the environment
-    simgrid::kernel::actor::simcall(std::bind(&HostEnergy::update, this));
+  if (last_updated_ < simgrid::s4u::Engine::get_clock()) // We need to simcall this as it modifies the environment
+    simgrid::kernel::actor::simcall_answered(std::bind(&HostEnergy::update, this));
 
   return total_energy_;
 }
@@ -390,8 +403,7 @@ void HostEnergy::init_watts_range_list()
 
   has_pstate_power_values_ = true;
 }
-} // namespace plugin
-} // namespace simgrid
+} // namespace simgrid::plugin
 
 using simgrid::plugin::HostEnergy;
 
@@ -409,18 +421,17 @@ static void on_creation(simgrid::s4u::Host& host)
 static void on_action_state_change(simgrid::kernel::resource::CpuAction const& action,
                                    simgrid::kernel::resource::Action::State /*previous*/)
 {
-  for (simgrid::kernel::resource::CpuImpl* const& cpu : action.cpus()) {
+  for (simgrid::kernel::resource::CpuImpl const* cpu : action.cpus()) {
     simgrid::s4u::Host* host = cpu->get_iface();
     if (host != nullptr) {
       // If it's a VM, take the corresponding PM
-      const simgrid::s4u::VirtualMachine* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host);
-      if (vm) // If it's a VM, take the corresponding PM
+      if (const auto* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host))
         host = vm->get_pm();
 
       // Get the host_energy extension for the relevant host
       auto* host_energy = host->extension<HostEnergy>();
 
-      if (host_energy->get_last_update_time() < surf_get_clock())
+      if (host_energy->get_last_update_time() < simgrid::s4u::Engine::get_clock())
         host_energy->update();
     }
   }
@@ -428,14 +439,13 @@ static void on_action_state_change(simgrid::kernel::resource::CpuAction const& a
 
 /* 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 on_host_change(simgrid::s4u::Host const& host)
+static void on_host_change(simgrid::s4u::Host const& h)
 {
-  if (dynamic_cast<simgrid::s4u::VirtualMachine const*>(&host)) // Ignore virtual machines
-    return;
+  auto* host = &h;
+  if (const auto* vm = dynamic_cast<simgrid::s4u::VirtualMachine const*>(host)) // Take the PM of virtual machines
+    host = vm->get_pm();
 
-  auto* host_energy = host.extension<HostEnergy>();
-
-  host_energy->update();
+  host->extension<HostEnergy>()->update();
 }
 
 static void on_host_destruction(simgrid::s4u::Host const& host)
@@ -463,6 +473,13 @@ static void on_simulation_end()
            used_hosts_energy, total_energy - used_hosts_energy);
 }
 
+static void on_activity_suspend_resume(simgrid::s4u::Activity const& activity)
+{
+  if (auto* action = dynamic_cast<simgrid::kernel::resource::CpuAction*>(activity.get_impl()->model_action_);
+      action != nullptr)
+    on_action_state_change(*action, /*ignored*/ action->get_state());
+}
+
 /* **************************** Public interface *************************** */
 
 /** @ingroup plugin_host_energy
@@ -476,23 +493,26 @@ void sg_host_energy_plugin_init()
 
   HostEnergy::EXTENSION_ID = simgrid::s4u::Host::extension_create<HostEnergy>();
 
-  simgrid::s4u::Host::on_creation.connect(&on_creation);
-  simgrid::s4u::Host::on_state_change.connect(&on_host_change);
-  simgrid::s4u::Host::on_speed_change.connect(&on_host_change);
-  simgrid::s4u::Host::on_destruction.connect(&on_host_destruction);
-  simgrid::s4u::Engine::on_simulation_end.connect(&on_simulation_end);
-  simgrid::kernel::resource::CpuAction::on_state_change.connect(&on_action_state_change);
+  simgrid::s4u::Host::on_creation_cb(&on_creation);
+  simgrid::s4u::Host::on_onoff_cb(&on_host_change);
+  simgrid::s4u::Host::on_speed_change_cb(&on_host_change);
+  simgrid::s4u::Host::on_destruction_cb(&on_host_destruction);
+  simgrid::s4u::Host::on_exec_state_change_cb(&on_action_state_change);
+  simgrid::s4u::VirtualMachine::on_suspend_cb(&on_host_change);
+  simgrid::s4u::VirtualMachine::on_resume_cb(&on_host_change);
+  simgrid::s4u::Exec::on_suspend_cb(on_activity_suspend_resume);
+  simgrid::s4u::Exec::on_resume_cb(on_activity_suspend_resume);
+  simgrid::s4u::Engine::on_simulation_end_cb(&on_simulation_end);
   // We may only have one actor on a node. If that actor executes something like
   //   compute -> recv -> compute
-  // the recv operation will not trigger a "CpuAction::on_state_change". This means
+  // the recv operation will not trigger a "Host::on_exec_state_change_cb". This means
   // that the next trigger would be the 2nd compute, hence ignoring the idle time
   // during the recv call. By updating at the beginning of a compute, we can
   // fix that. (If the cpu is not idle, this is not required.)
-  simgrid::s4u::Exec::on_start.connect([](simgrid::s4u::Exec const& activity) {
+  simgrid::s4u::Exec::on_start_cb([](simgrid::s4u::Exec const& activity) {
     if (activity.get_host_number() == 1) { // We only run on one host
-      simgrid::s4u::Host* host         = activity.get_host();
-      const simgrid::s4u::VirtualMachine* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host);
-      if (vm != nullptr)
+      simgrid::s4u::Host* host = activity.get_host();
+      if (const auto* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host))
         host = vm->get_pm();
       xbt_assert(host != nullptr);
       host->extension<HostEnergy>()->update();
@@ -508,7 +528,7 @@ void sg_host_energy_plugin_init()
  */
 void sg_host_energy_update_all()
 {
-  simgrid::kernel::actor::simcall([]() {
+  simgrid::kernel::actor::simcall_answered([]() {
     std::vector<simgrid::s4u::Host*> list = simgrid::s4u::Engine::get_instance()->get_all_hosts();
     for (auto const& host : list)
       if (dynamic_cast<simgrid::s4u::VirtualMachine*>(host) == nullptr) { // Ignore virtual machines