Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' into actor-yield
[simgrid.git] / src / surf / plugins / link_energy.cpp
1 /* Copyright (c) 2017. 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/plugins/energy.h"
7 #include "simgrid/s4u/Engine.hpp"
8 #include "simgrid/simix.hpp"
9 #include "src/surf/network_interface.hpp"
10 #include <boost/algorithm/string/classification.hpp>
11 #include <boost/algorithm/string/split.hpp>
12 #include <map>
13 #include <string>
14 #include <utility>
15 #include <vector>
16
17 /** @addtogroup SURF_plugin_energy
18
19
20  This is the energy plugin, enabling to account for the dissipated energy in the simulated platform.
21
22  The energy consumption of a link depends directly on its current traffic load. Specify that consumption in your
23  platform file as follows:
24
25  \verbatim
26  <link id="SWITCH1" bandwidth="125000000" latency="5E-5" sharing_policy="SHARED" >
27  <prop id="watts" value="100.0:200.0" />
28  <prop id="watt_off" value="10" />
29  </link>
30  \endverbatim
31
32  The first property means that when your link is switched on, but without anything to do, it will dissipate 100 Watts.
33  If it's fully loaded, it will dissipate 200 Watts. If its load is at 50%, then it will dissipate 150 Watts.
34  The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these
35  values are arbitrary).
36
37  To simulate the energy-related elements, first call the simgrid#energy#sg_link_energy_plugin_init() before your
38  #MSG_init(),
39  and then use the following function to retrieve the consumption of a given link: MSG_link_get_consumed_energy().
40  */
41
42 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(link_energy, surf, "Logging specific to the SURF LinkEnergy plugin");
43
44 namespace simgrid {
45 namespace plugin {
46
47 class LinkPowerRange {
48 public:
49   double idle;
50   double busy;
51
52   LinkPowerRange(double idle, double busy) : idle(idle), busy(busy) {}
53 };
54
55 class LinkEnergy {
56 public:
57   static simgrid::xbt::Extension<simgrid::s4u::Link, LinkEnergy> EXTENSION_ID;
58
59   explicit LinkEnergy(simgrid::s4u::Link* ptr);
60   ~LinkEnergy();
61
62   double getALinkTotalPower();
63   void initWattsRangeList();
64   double getTotalEnergy();
65   void update();
66
67 private:
68   double getPower();
69
70   simgrid::s4u::Link* link{};
71
72   std::vector<LinkPowerRange> power_range_watts_list{};
73
74   double total_energy{0.0};
75   double last_updated{0.0}; /*< Timestamp of the last energy update event*/
76 };
77
78 simgrid::xbt::Extension<simgrid::s4u::Link, LinkEnergy> LinkEnergy::EXTENSION_ID;
79
80 LinkEnergy::LinkEnergy(simgrid::s4u::Link* ptr) : link(ptr), last_updated(surf_get_clock())
81 {
82 }
83
84 LinkEnergy::~LinkEnergy() = default;
85
86 void LinkEnergy::update()
87 {
88   double power = getPower();
89   double now   = surf_get_clock();
90   total_energy += power * (now - last_updated);
91   last_updated = now;
92 }
93
94 void LinkEnergy::initWattsRangeList()
95 {
96
97   if (!power_range_watts_list.empty())
98     return;
99
100   const char* all_power_values_str = this->link->getProperty("watt_range");
101
102   if (all_power_values_str == nullptr)
103     return;
104
105   std::vector<std::string> all_power_values;
106   boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
107
108   for (auto current_power_values_str : all_power_values) {
109     /* retrieve the power values associated */
110     std::vector<std::string> current_power_values;
111     boost::split(current_power_values, current_power_values_str, boost::is_any_of(":"));
112     xbt_assert(current_power_values.size() == 2, "Power properties incorrectly defined - "
113                                                  "could not retrieve idle and busy power values for link %s",
114                this->link->getCname());
115
116     /* min_power corresponds to the idle power (link load = 0) */
117     /* max_power is the power consumed at 100% link load       */
118     char* idle = bprintf("Invalid idle power value for link%s", this->link->getCname());
119     char* busy = bprintf("Invalid busy power value for %s", this->link->getCname());
120
121     double idleVal = xbt_str_parse_double((current_power_values.at(0)).c_str(), idle);
122
123     double busyVal = xbt_str_parse_double((current_power_values.at(1)).c_str(), busy);
124
125     this->power_range_watts_list.push_back(LinkPowerRange(idleVal, busyVal));
126
127     xbt_free(idle);
128     xbt_free(busy);
129     update();
130   }
131 }
132
133 double LinkEnergy::getPower()
134 {
135
136   if (power_range_watts_list.empty())
137     return 0.0;
138
139   auto range = power_range_watts_list[0];
140
141   double busy = range.busy;
142   double idle = range.idle;
143
144   double power_slope = busy - idle;
145
146   double normalized_link_usage = link->getUsage() / link->bandwidth();
147   double dynamic_power         = power_slope * normalized_link_usage;
148
149   return idle + dynamic_power;
150 }
151
152 double LinkEnergy::getTotalEnergy()
153 {
154   update();
155   return this->total_energy;
156 }
157 }
158 }
159
160 using simgrid::plugin::LinkEnergy;
161
162 /* **************************** events  callback *************************** */
163 static void onCreation(simgrid::s4u::Link& link)
164 {
165   XBT_DEBUG("onCreation is called for link: %s", link.getCname());
166   link.extension_set(new LinkEnergy(&link));
167 }
168
169 static void onCommunicate(simgrid::surf::NetworkAction* action, simgrid::s4u::Host* src, simgrid::s4u::Host* dst)
170 {
171   XBT_DEBUG("onCommunicate is called");
172   for (simgrid::surf::LinkImpl* link : action->links()) {
173
174     if (link == nullptr)
175       continue;
176
177     XBT_DEBUG("Update link %s", link->getCname());
178     // Get the link_energy extension for the relevant link
179     LinkEnergy* link_energy = link->piface_.extension<LinkEnergy>();
180     link_energy->initWattsRangeList();
181     link_energy->update();
182   }
183 }
184
185 static void onActionStateChange(simgrid::surf::NetworkAction* action)
186 {
187   XBT_DEBUG("onActionStateChange is called");
188   for (simgrid::surf::LinkImpl* link : action->links()) {
189
190     if (link == nullptr)
191       continue;
192
193     // Get the link_energy extension for the relevant link
194     LinkEnergy* link_energy = link->piface_.extension<LinkEnergy>();
195     link_energy->update();
196   }
197 }
198
199 static void onLinkStateChange(simgrid::s4u::Link& link)
200 {
201   XBT_DEBUG("onLinkStateChange is called for link: %s", link.getCname());
202
203   LinkEnergy* link_energy = link.extension<LinkEnergy>();
204   link_energy->update();
205 }
206
207 static void onLinkDestruction(simgrid::s4u::Link& link)
208 {
209   XBT_DEBUG("onLinkDestruction is called for link: %s", link.getCname());
210
211   LinkEnergy* link_energy = link.extension<LinkEnergy>();
212   link_energy->update();
213 }
214
215 static void computeAndDisplayTotalEnergy()
216 {
217   std::vector<simgrid::s4u::Link*> link_list;
218   simgrid::s4u::Engine::getInstance()->getLinkList(&link_list);
219   double total_energy = 0.0; // Total dissipated energy (whole platform)
220   for (const auto link : link_list) {
221     LinkEnergy* link_energy = link->extension<LinkEnergy>();
222
223     double a_link_total_energy = link_energy->getTotalEnergy();
224     total_energy += a_link_total_energy;
225     const char* name = link->getCname();
226     if (strcmp(name, "__loopback__"))
227       XBT_INFO("Link '%s' total consumption: %f", name, a_link_total_energy);
228   }
229
230   XBT_INFO("Total energy over all links: %f", total_energy);
231 }
232
233 static void onSimulationEnd()
234 {
235   computeAndDisplayTotalEnergy();
236 }
237 /* **************************** Public interface *************************** */
238 SG_BEGIN_DECL()
239 /** \ingroup SURF_plugin_energy
240  * \brief Enable energy plugin
241  * \details Enable energy plugin to get joules consumption of each cpu. You should call this function before
242  * #MSG_init().
243  */
244 void sg_link_energy_plugin_init()
245 {
246
247   if (LinkEnergy::EXTENSION_ID.valid())
248     return;
249   LinkEnergy::EXTENSION_ID = simgrid::s4u::Link::extension_create<LinkEnergy>();
250
251   simgrid::s4u::Link::onCreation.connect(&onCreation);
252   simgrid::s4u::Link::onStateChange.connect(&onLinkStateChange);
253   simgrid::s4u::Link::onDestruction.connect(&onLinkDestruction);
254   simgrid::s4u::Link::onCommunicationStateChange.connect(&onActionStateChange);
255   simgrid::s4u::Link::onCommunicate.connect(&onCommunicate);
256   simgrid::s4u::onSimulationEnd.connect(&onSimulationEnd);
257 }
258
259 SG_END_DECL()