Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
6487ce74119fd3475e5db55c7404b723a243f1bd
[simgrid.git] / src / surf / plugins / link_energy.cpp
1 /* Copyright (c) 2010, 2012-2016. 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/link_energy.h"
7 #include "simgrid/simix.hpp"
8 #include "src/surf/network_interface.hpp"
9 #include "simgrid/s4u/Engine.hpp"
10 #include <boost/algorithm/string/classification.hpp>
11 #include <boost/algorithm/string/split.hpp>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 #include <map>
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 platform file as follows:
23
24  \verbatim
25  <link id="SWITCH1" bandwidth="125000000" latency="5E-5" sharing_policy="SHARED" >
26  <prop id="watts" value="100.0:200.0" />
27  <prop id="watt_off" value="10" />
28  </link>
29  \endverbatim
30
31  The first property means that when your link is switched on, but without anything to do, it will dissipate 100 Watts.
32  If it's fully loaded, it will dissipate 200 Watts. If its load is at 50%, then it will dissipate 150 Watts.
33  The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these
34  values are arbitrary).
35
36  To simulate the energy-related elements, first call the simgrid#energy#sg_link_energy_plugin_init() before your #MSG_init(),
37  and then use the following function to retrieve the consumption of a given link: MSG_link_get_consumed_energy().
38  */
39
40 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(link_energy, surf,
41                 "Logging specific to the SURF LinkEnergy plugin");
42
43 namespace simgrid {
44 namespace plugin {
45
46 class LinkPowerRange {
47 public:
48         double idle;
49         double busy;
50
51         LinkPowerRange(double idle, double busy) :
52                         idle(idle), busy(busy) {
53         }
54 };
55
56 class LinkEnergy {
57 public:
58         static simgrid::xbt::Extension<simgrid::s4u::Link, LinkEnergy> EXTENSION_ID;
59
60         explicit LinkEnergy(simgrid::s4u::Link *ptr);
61         ~LinkEnergy();
62
63         double getAveragePower(sg_link_t link);
64
65
66         double getALinkTotalPower(sg_link_t link);
67         void initWattsRangeList();
68         double getLinkUsage();
69         double getALinkTotalEnergy(sg_link_t link);
70         void update();
71 private:
72         double computeALinkPower();
73         void computeALinkTotalEnergy();
74
75
76
77         simgrid::s4u::Link *link { };
78 //      simgrid::s4u::Link *up_link { };
79 //      simgrid::s4u::Link *down_link { };
80
81         std::vector<LinkPowerRange> power_range_watts_list { };
82
83         std::map<const char*, double> a_link_average_power { };
84
85
86         std::map<const char*, double> a_link_total_energy { };
87
88         double last_updated { 0.0 }; /*< Timestamp of the last energy update event*/
89         double current_link_usage { 0.0 };
90
91 };
92
93 simgrid::xbt::Extension<simgrid::s4u::Link, LinkEnergy> LinkEnergy::EXTENSION_ID;
94 //string replacing function
95 void replace(std::string& str, const std::string& from, const std::string& to,
96                 size_t start_pos) {
97         str.replace(start_pos, from.length(), to);
98 }
99
100 LinkEnergy::LinkEnergy(simgrid::s4u::Link *ptr) :
101                 link(ptr), last_updated(surf_get_clock()) {
102
103         /*std::string lnk_name(this->link->name());
104          size_t lnk_down = lnk_name.find("_DOWN");
105          size_t lnk_up = lnk_name.find("_UP");
106
107          if (lnk_down != std::string::npos) {
108
109          this->down_link = this->link->byName(lnk_name.c_str());
110          replace(lnk_name, "_DOWN", "_UP", lnk_down);
111          this->up_link = this->link->byName(lnk_name.c_str());
112
113          } else if (lnk_up != std::string::npos) {
114
115          this->up_link = this->link->byName(lnk_name.c_str());
116          replace(lnk_name, "_UP", "_DOWN", lnk_up);
117          this->down_link = this->link->byName(lnk_name.c_str());
118
119          } else {
120          this->up_link = this->link;
121          }*/
122
123 }
124
125 LinkEnergy::~LinkEnergy() = default;
126
127 void LinkEnergy::update() {
128
129         this->current_link_usage = this->link->getUsage();
130
131         computeALinkTotalEnergy();
132
133         /*
134          double uplink_usage{0.0};
135          double downlink_usage{0.0};
136
137          std::string lnk_name(this->link->name());
138          size_t lnk_down = lnk_name.find("_DOWN");
139          size_t lnk_up = lnk_name.find("_UP");
140
141          if (lnk_down != std::string::npos) {
142
143          downlink_usage = lmm_constraint_get_usage(
144          this->down_link->pimpl_->constraint());
145
146          this->up_link->extension<LinkEnergy>()->updateLinkUsage();
147
148          } else if (lnk_up != std::string::npos) {
149
150          uplink_usage = lmm_constraint_get_usage(
151          this->up_link->pimpl_->constraint());
152
153          this->link_usage = downlink_usage + uplink_usage;
154
155          } else {
156
157          this->link_usage = lmm_constraint_get_usage(
158          this->up_link->pimpl_->constraint());
159
160          }*/
161 }
162
163 void LinkEnergy::initWattsRangeList() {
164
165         if (!power_range_watts_list.empty())
166                 return;
167
168         xbt_assert(power_range_watts_list.empty(),
169                         "Power properties incorrectly defined - "
170                                         "could not retrieve idle and busy power values for link %s",
171                         this->link->getCname());
172
173         const char* all_power_values_str = this->link->property("watt_range");
174
175         if (all_power_values_str == nullptr)
176                 return;
177
178         std::vector<std::string> all_power_values;
179         boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
180
181         for (auto current_power_values_str : all_power_values) {
182                 /* retrieve the power values associated */
183                 std::vector<std::string> current_power_values;
184                 boost::split(current_power_values, current_power_values_str,
185                                 boost::is_any_of(":"));
186                 xbt_assert(current_power_values.size() == 2,
187                                 "Power properties incorrectly defined - "
188                                                 "could not retrieve idle and busy power values for link %s",
189                                 this->link->getCname());
190
191                 /* min_power corresponds to the idle power (link load = 0) */
192                 /* max_power is the power consumed at 100% link load       */
193                 char* idle = bprintf("Invalid idle power value for link%s",
194                                 this->link->getCname());
195                 char* busy = bprintf("Invalid busy power value for %s",
196                                 this->link->getCname());
197
198                 double idleVal = xbt_str_parse_double(
199                                 (current_power_values.at(0)).c_str(), idle);
200
201
202                 idleVal *= 2; // the idle value is multiplied by 2 because SimGrid's 1 link is mapped to 2 NetDevices in ECOFEN
203                 double busyVal = xbt_str_parse_double(
204                                 (current_power_values.at(1)).c_str(), busy);
205                 busyVal *= 2; // the busy value is multiplied by 2 because SimGrid's 1 link is mapped to 2 NetDevices in ECOFEN
206
207
208                 this->power_range_watts_list.push_back(
209                                 LinkPowerRange(idleVal, busyVal));
210                 this->a_link_average_power[this->link->getCname()] = idleVal;
211
212
213                 xbt_free(idle);
214                 xbt_free(busy);
215                 update();
216
217         }
218
219 }
220
221 double LinkEnergy::computeALinkPower(){
222
223         if (!strcmp(this->link->getCname(), "__loopback__"))
224                         return 0.0;
225
226                 double dynamic_power = 0.0;
227
228                 if (power_range_watts_list.empty()) {
229                         return 0.0;
230                 }
231
232                 xbt_assert(!power_range_watts_list.empty(),
233                                 "No power range properties specified for link %s",
234                                 this->link->getCname());
235
236                 auto range = power_range_watts_list[0];
237
238                 double busy = range.busy;
239                 double idle = range.idle;
240
241                 double power_slope = busy - idle;
242
243                 if (this->last_updated > 0) {
244
245                         double normalized_link_usage = this->current_link_usage
246                                         / this->link->bandwidth();
247                         dynamic_power = power_slope * normalized_link_usage;
248
249                 } else {
250
251                         dynamic_power = 0.0;
252                 }
253                 double current_power = idle + dynamic_power;
254
255                 return current_power;
256
257 }
258
259 void LinkEnergy::computeALinkTotalEnergy() {
260
261         double current_power = computeALinkPower();
262         double now = surf_get_clock();
263         double a_link_total_energy = current_power * (now - last_updated);
264         this->a_link_total_energy[this->link->getCname()] += a_link_total_energy;
265         last_updated = now;
266 }
267
268 double LinkEnergy::getLinkUsage() {
269         return this->current_link_usage;
270 }
271
272 double LinkEnergy::getAveragePower(sg_link_t link) {
273
274         return this->a_link_average_power[link->getCname()];
275 }
276
277 double LinkEnergy::getALinkTotalEnergy(sg_link_t link) {
278         return this->a_link_total_energy[link->getCname()];
279
280 }
281
282 }
283 }
284
285 using simgrid::plugin::LinkEnergy;
286
287 /* **************************** events  callback *************************** */
288 static void onCreation(simgrid::s4u::Link& link) {
289         XBT_DEBUG("onCreation is called for link: %s", link.getCname());
290         link.extension_set(new LinkEnergy(&link));
291 }
292
293 static void onCommunicate(simgrid::surf::NetworkAction* action,
294                 simgrid::s4u::Host* src, simgrid::s4u::Host* dst) {
295         XBT_DEBUG("onCommunicate is called");
296         for (simgrid::surf::LinkImpl* link : action->links()) {
297
298                 if (link == nullptr)
299                         continue;
300
301                 // Get the link_energy extension for the relevant link
302                 LinkEnergy* link_energy = link->piface_.extension<LinkEnergy>();
303                 link_energy->initWattsRangeList();
304                 link_energy->update();
305         }
306 }
307
308 static void onActionStateChange(simgrid::surf::NetworkAction* action) {
309         XBT_DEBUG("onActionStateChange is called");
310         for (simgrid::surf::LinkImpl* link : action->links()) {
311
312                 if (link == nullptr)
313                         continue;
314
315                 // Get the link_energy extension for the relevant link
316                 LinkEnergy* link_energy = link->piface_.extension<LinkEnergy>();
317                 link_energy->update();
318         }
319 }
320
321 static void onLinkStateChange(simgrid::s4u::Link &link) {
322         XBT_DEBUG("onLinkStateChange is called for link: %s", link.getCname());
323
324         LinkEnergy *link_energy = link.extension<LinkEnergy>();
325         link_energy->update();
326 }
327
328 static void onLinkDestruction(simgrid::s4u::Link& link) {
329         XBT_DEBUG("onLinkDestruction is called for link: %s", link.getCname());
330
331         LinkEnergy *link_energy = link.extension<LinkEnergy>();
332         link_energy->update();
333 }
334
335
336 static void computAndDisplayTotalEnergy() {
337         simgrid::s4u::Link* link = nullptr;
338         sg_link_t* link_list = link->listLink();
339         int link_count = link->linkCount();
340         double total_power = 0.0; // Total power consumption (whole platform)
341         double total_energy = 0.0;
342         double total_time = 0.0;
343         for (int i = 0; i < link_count; i++) {
344                 if (link_list[i] != nullptr) {
345                         LinkEnergy* link_energy = link_list[i]->extension<LinkEnergy>();
346                         link_energy->update();
347
348                         double a_link_average_power = link_energy->getAveragePower(
349                                         link_list[i]);
350                         total_power += a_link_average_power;
351
352                         double a_link_total_energy = link_energy->getALinkTotalEnergy(
353                                         link_list[i]);
354                         total_energy += a_link_total_energy;
355                         const char* name = link_list[i]->getCname();
356                         if (strcmp(name, "__loopback__")) {
357                                 XBT_INFO("%s Usage %f Bandwidth %f Power %f Energy %f", name,
358                                                 link_energy->getLinkUsage(), link_list[i]->bandwidth(),
359                                                 a_link_average_power, a_link_total_energy);
360                         }
361                 }
362         }
363
364         XBT_INFO("SgTotalPower %f SgTotalEnergy %f SgTransferTime %f", total_power,
365                         total_energy, surf_get_clock());
366         xbt_free(link_list);
367 }
368
369 static void onSimulationEnd() {
370         computAndDisplayTotalEnergy();
371 }
372 /* **************************** Public interface *************************** */
373 SG_BEGIN_DECL()
374 /** \ingroup SURF_plugin_energy
375  * \brief Enable energy plugin
376  * \details Enable energy plugin to get joules consumption of each cpu. You should call this function before #MSG_init().
377  */
378 void sg_link_energy_plugin_init() {
379
380         if (LinkEnergy::EXTENSION_ID.valid())
381                 return;
382         LinkEnergy::EXTENSION_ID =
383                         simgrid::s4u::Link::extension_create<LinkEnergy>();
384
385         simgrid::s4u::Link::onCreation.connect(&onCreation);
386         simgrid::s4u::Link::onStateChange.connect(&onLinkStateChange);
387         simgrid::s4u::Link::onDestruction.connect(&onLinkDestruction);
388         simgrid::s4u::Link::onCommunicationStateChange.connect(
389                         &onActionStateChange);
390         simgrid::s4u::Link::onCommunicate.connect(&onCommunicate);
391         simgrid::s4u::onSimulationEnd.connect(&onSimulationEnd);
392
393 }
394
395 /** @brief Returns the total energy consumed by the link so far (in Joules)
396  *
397  *  See also @ref SURF_plugin_energy.
398  */
399
400 double sg_link_get_usage(sg_link_t link) {
401         xbt_assert(LinkEnergy::EXTENSION_ID.valid(),
402                         "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
403         LinkEnergy *link_energy = link->extension<LinkEnergy>();
404         return link_energy->getLinkUsage();
405 }
406
407 SG_END_DECL()