Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
move the models to the right directory (empty src/surf a bit)
[simgrid.git] / src / plugins / link_energy.cpp
1 /* Copyright (c) 2017-2023. The SimGrid Team. All rights reserved.          */
2
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. */
5
6 #include "simgrid/Exception.hpp"
7 #include "simgrid/host.h"
8 #include "simgrid/plugins/energy.h"
9 #include "simgrid/s4u/Engine.hpp"
10 #include "simgrid/s4u/Link.hpp"
11 #include "src/kernel/activity/CommImpl.hpp"
12 #include "src/simgrid/module.hpp"
13
14 #include <boost/algorithm/string/classification.hpp>
15 #include <boost/algorithm/string/split.hpp>
16
17 SIMGRID_REGISTER_PLUGIN(link_energy, "Link energy consumption.", &sg_link_energy_plugin_init)
18
19 /** @defgroup plugin_link_energy Plugin Link Energy
20
21  This is the link energy plugin, accounting for the dissipated energy in the simulated platform.
22
23  The energy consumption of a link depends directly on its current traffic load. Specify that consumption in your
24  platform file as follows:
25
26  @verbatim
27  <link id="SWITCH1" bandwidth="125Mbps" latency="5us" sharing_policy="SHARED" >
28  <prop id="wattage_range" value="100.0:200.0" />
29  <prop id="wattage_off" value="10" />
30  </link>
31  @endverbatim
32
33  The first property means that when your link is switched on, but without anything to do, it will dissipate 100 Watts.
34  If it's fully loaded, it will dissipate 200 Watts. If its load is at 50%, then it will dissipate 150 Watts.
35  The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these
36  values are arbitrary).
37
38  To simulate the energy-related elements, first call the sg_link_energy_plugin_init() before loading the platform
39  and then use the following function to retrieve the consumption of a given link: sg_link_get_consumed_energy().
40  */
41
42 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(link_energy, kernel, "Logging specific to the LinkEnergy plugin");
43
44 namespace simgrid::plugin {
45
46 class LinkEnergy {
47   s4u::Link* link_{};
48
49   bool inited_{false};
50   double idle_{0.0};
51   double busy_{0.0};
52
53   double total_energy_{0.0};
54   double last_updated_{0.0}; /*< Timestamp of the last energy update event*/
55
56   double get_power() const;
57
58 public:
59   static xbt::Extension<simgrid::s4u::Link, LinkEnergy> EXTENSION_ID;
60
61   explicit LinkEnergy(s4u::Link* ptr) : link_(ptr), last_updated_(simgrid::s4u::Engine::get_clock()) {}
62
63   void init_watts_range_list();
64   double get_consumed_energy();
65   void update();
66 };
67
68 xbt::Extension<s4u::Link, LinkEnergy> LinkEnergy::EXTENSION_ID;
69
70 void LinkEnergy::update()
71 {
72   if (not inited_)
73     init_watts_range_list();
74
75   double power = get_power();
76   double now   = simgrid::s4u::Engine::get_clock();
77   total_energy_ += power * (now - last_updated_);
78   last_updated_ = now;
79 }
80
81 void LinkEnergy::init_watts_range_list()
82 {
83   if (inited_)
84     return;
85   inited_ = true;
86
87   const char* all_power_values_str = this->link_->get_property("wattage_range");
88   if (all_power_values_str == nullptr) {
89     all_power_values_str = this->link_->get_property("watt_range");
90     if (all_power_values_str != nullptr)
91       XBT_WARN("Please rename the 'watt_range' property of link %s into 'wattage_range'.", link_->get_cname());
92   }
93
94   if (all_power_values_str == nullptr)
95     return;
96
97   std::vector<std::string> all_power_values;
98   boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
99
100   for (auto current_power_values_str : all_power_values) {
101     /* retrieve the power values associated */
102     std::vector<std::string> current_power_values;
103     boost::split(current_power_values, current_power_values_str, boost::is_any_of(":"));
104     xbt_assert(current_power_values.size() == 2,
105                "Power properties incorrectly defined - could not retrieve idle and busy power values for link %s",
106                this->link_->get_cname());
107
108     /* min_power corresponds to the idle power (link load = 0) */
109     /* max_power is the power consumed at 100% link load       */
110     try {
111       idle_ = std::stod(current_power_values.front());
112     } catch (const std::invalid_argument&) {
113       throw std::invalid_argument("Invalid idle power value for link " + this->link_->get_name());
114     }
115
116     try {
117       busy_ = std::stod(current_power_values.back());
118     } catch (const std::invalid_argument&) {
119       throw std::invalid_argument("Invalid busy power value for link " + this->link_->get_name());
120     }
121   }
122 }
123
124 double LinkEnergy::get_power() const
125 {
126   if (not inited_)
127     return 0.0;
128
129   double power_slope = busy_ - idle_;
130
131   double normalized_link_usage = link_->get_usage() / link_->get_bandwidth();
132   double dynamic_power         = power_slope * normalized_link_usage;
133
134   return idle_ + dynamic_power;
135 }
136
137 double LinkEnergy::get_consumed_energy()
138 {
139   if (last_updated_ < simgrid::s4u::Engine::get_clock()) // We need to simcall this as it modifies the environment
140     kernel::actor::simcall_answered(std::bind(&LinkEnergy::update, this));
141   return this->total_energy_;
142 }
143 } // namespace simgrid::plugin
144
145 using simgrid::plugin::LinkEnergy;
146
147 /* **************************** events  callback *************************** */
148 static void on_simulation_end()
149 {
150   std::vector<simgrid::s4u::Link*> links = simgrid::s4u::Engine::get_instance()->get_all_links();
151
152   double total_energy = 0.0; // Total dissipated energy (whole platform)
153   for (auto const* link : links) {
154     if (link == nullptr || link->get_sharing_policy() == simgrid::s4u::Link::SharingPolicy::WIFI)
155       continue;
156
157     double link_energy = link->extension<LinkEnergy>()->get_consumed_energy();
158     total_energy += link_energy;
159   }
160
161   XBT_INFO("Total energy over all links: %f", total_energy);
162 }
163
164 static void on_communication(const simgrid::kernel::activity::CommImpl& comm)
165 {
166   for (auto const* link : comm.get_traversed_links()) {
167     if (link != nullptr && link->get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
168       XBT_DEBUG("Update %s on Comm Start/End", link->get_cname());
169       link->extension<LinkEnergy>()->update();
170     }
171   }
172 }
173 /* **************************** Public interface *************************** */
174
175 int sg_link_energy_is_inited()
176 {
177   return LinkEnergy::EXTENSION_ID.valid();
178 }
179 /** @ingroup plugin_link_energy
180  * @brief Enable energy plugin
181  * @details Enable energy plugin to get joules consumption of each cpu. You should call this function before
182  * loading your platform.
183  */
184 void sg_link_energy_plugin_init()
185 {
186   if (LinkEnergy::EXTENSION_ID.valid())
187     return;
188   LinkEnergy::EXTENSION_ID = simgrid::s4u::Link::extension_create<LinkEnergy>();
189
190   xbt_assert(sg_host_count() == 0, "Please call sg_link_energy_plugin_init() before initializing the platform.");
191
192   simgrid::s4u::Link::on_creation_cb([](simgrid::s4u::Link& link) {
193     if (link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
194       XBT_DEBUG("Wired Link created: %s", link.get_cname());
195       link.extension_set(new LinkEnergy(&link));
196     } else {
197       XBT_DEBUG("Not Wired link: %s", link.get_cname());
198     }
199   });
200
201   simgrid::s4u::Link::on_state_change_cb([](simgrid::s4u::Link const& link) {
202     if (link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI)
203       link.extension<LinkEnergy>()->update();
204   });
205
206   simgrid::s4u::Link::on_destruction_cb([](simgrid::s4u::Link const& link) {
207     if (link.get_name() != "__loopback__" && link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI)
208       XBT_INFO("Energy consumption of link '%s': %f Joules", link.get_cname(),
209                link.extension<LinkEnergy>()->get_consumed_energy());
210   });
211
212   simgrid::kernel::activity::CommImpl::on_start.connect(&on_communication);
213   simgrid::kernel::activity::CommImpl::on_completion.connect(&on_communication);
214
215   simgrid::s4u::Engine::on_simulation_end_cb(&on_simulation_end);
216 }
217
218 /** @ingroup plugin_link_energy
219  *  @brief Returns the total energy consumed by the link so far (in Joules)
220  *
221  *  Please note that since the consumption is lazily updated, it may require a simcall to update it.
222  *  The result is that the actor requesting this value will be interrupted,
223  *  the value will be updated in kernel mode before returning the control to the requesting actor.
224  */
225 double sg_link_get_consumed_energy(const_sg_link_t link)
226 {
227   if (not LinkEnergy::EXTENSION_ID.valid())
228     throw simgrid::xbt::InitializationError("The Energy plugin is not active. Please call sg_link_energy_plugin_init() "
229                                             "before calling sg_link_get_consumed_energy().");
230   return link->extension<LinkEnergy>()->get_consumed_energy();
231 }