1 /* Copyright (c) 2010, 2012-2016. The SimGrid Team. All rights reserved. */
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. */
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>
17 /** @addtogroup SURF_plugin_energy
20 This is the energy plugin, enabling to account for the dissipated energy in the simulated platform.
22 The energy consumption of a link depends directly on its current traffic load. Specify that consumption in your platform file as follows:
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" />
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).
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().
40 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(link_energy, surf,
41 "Logging specific to the SURF LinkEnergy plugin");
46 class LinkPowerRange {
51 LinkPowerRange(double idle, double busy) :
52 idle(idle), busy(busy) {
58 static simgrid::xbt::Extension<simgrid::s4u::Link, LinkEnergy> EXTENSION_ID;
60 explicit LinkEnergy(simgrid::s4u::Link *ptr);
63 double getAveragePower(sg_link_t link);
66 double getALinkTotalPower(sg_link_t link);
67 void initWattsRangeList();
68 double getLinkUsage();
69 double getALinkTotalEnergy(sg_link_t link);
72 double computeALinkPower();
73 void computeALinkTotalEnergy();
77 simgrid::s4u::Link *link { };
78 // simgrid::s4u::Link *up_link { };
79 // simgrid::s4u::Link *down_link { };
81 std::vector<LinkPowerRange> power_range_watts_list { };
83 std::map<const char*, double> a_link_average_power { };
86 std::map<const char*, double> a_link_total_energy { };
88 double last_updated { 0.0 }; /*< Timestamp of the last energy update event*/
89 double current_link_usage { 0.0 };
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,
97 str.replace(start_pos, from.length(), to);
100 LinkEnergy::LinkEnergy(simgrid::s4u::Link *ptr) :
101 link(ptr), last_updated(surf_get_clock()) {
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");
107 if (lnk_down != std::string::npos) {
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());
113 } else if (lnk_up != std::string::npos) {
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());
120 this->up_link = this->link;
125 LinkEnergy::~LinkEnergy() = default;
127 void LinkEnergy::update() {
129 this->current_link_usage = this->link->getUsage();
131 computeALinkTotalEnergy();
134 double uplink_usage{0.0};
135 double downlink_usage{0.0};
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");
141 if (lnk_down != std::string::npos) {
143 downlink_usage = lmm_constraint_get_usage(
144 this->down_link->pimpl_->constraint());
146 this->up_link->extension<LinkEnergy>()->updateLinkUsage();
148 } else if (lnk_up != std::string::npos) {
150 uplink_usage = lmm_constraint_get_usage(
151 this->up_link->pimpl_->constraint());
153 this->link_usage = downlink_usage + uplink_usage;
157 this->link_usage = lmm_constraint_get_usage(
158 this->up_link->pimpl_->constraint());
163 void LinkEnergy::initWattsRangeList() {
165 if (!power_range_watts_list.empty())
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());
173 const char* all_power_values_str = this->link->property("watt_range");
175 if (all_power_values_str == nullptr)
178 std::vector<std::string> all_power_values;
179 boost::split(all_power_values, all_power_values_str, boost::is_any_of(","));
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());
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());
198 double idleVal = xbt_str_parse_double(
199 (current_power_values.at(0)).c_str(), idle);
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
208 this->power_range_watts_list.push_back(
209 LinkPowerRange(idleVal, busyVal));
210 this->a_link_average_power[this->link->getCname()] = idleVal;
221 double LinkEnergy::computeALinkPower(){
223 if (!strcmp(this->link->getCname(), "__loopback__"))
226 double dynamic_power = 0.0;
228 if (power_range_watts_list.empty()) {
232 xbt_assert(!power_range_watts_list.empty(),
233 "No power range properties specified for link %s",
234 this->link->getCname());
236 auto range = power_range_watts_list[0];
238 double busy = range.busy;
239 double idle = range.idle;
241 double power_slope = busy - idle;
243 if (this->last_updated > 0) {
245 double normalized_link_usage = this->current_link_usage
246 / this->link->bandwidth();
247 dynamic_power = power_slope * normalized_link_usage;
253 double current_power = idle + dynamic_power;
255 return current_power;
259 void LinkEnergy::computeALinkTotalEnergy() {
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;
268 double LinkEnergy::getLinkUsage() {
269 return this->current_link_usage;
272 double LinkEnergy::getAveragePower(sg_link_t link) {
274 return this->a_link_average_power[link->getCname()];
277 double LinkEnergy::getALinkTotalEnergy(sg_link_t link) {
278 return this->a_link_total_energy[link->getCname()];
285 using simgrid::plugin::LinkEnergy;
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));
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()) {
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();
308 static void onActionStateChange(simgrid::surf::NetworkAction* action) {
309 XBT_DEBUG("onActionStateChange is called");
310 for (simgrid::surf::LinkImpl* link : action->links()) {
315 // Get the link_energy extension for the relevant link
316 LinkEnergy* link_energy = link->piface_.extension<LinkEnergy>();
317 link_energy->update();
321 static void onLinkStateChange(simgrid::s4u::Link &link) {
322 XBT_DEBUG("onLinkStateChange is called for link: %s", link.getCname());
324 LinkEnergy *link_energy = link.extension<LinkEnergy>();
325 link_energy->update();
328 static void onLinkDestruction(simgrid::s4u::Link& link) {
329 XBT_DEBUG("onLinkDestruction is called for link: %s", link.getCname());
331 LinkEnergy *link_energy = link.extension<LinkEnergy>();
332 link_energy->update();
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();
348 double a_link_average_power = link_energy->getAveragePower(
350 total_power += a_link_average_power;
352 double a_link_total_energy = link_energy->getALinkTotalEnergy(
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);
364 XBT_INFO("SgTotalPower %f SgTotalEnergy %f SgTransferTime %f", total_power,
365 total_energy, surf_get_clock());
369 static void onSimulationEnd() {
370 computAndDisplayTotalEnergy();
372 /* **************************** Public interface *************************** */
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().
378 void sg_link_energy_plugin_init() {
380 if (LinkEnergy::EXTENSION_ID.valid())
382 LinkEnergy::EXTENSION_ID =
383 simgrid::s4u::Link::extension_create<LinkEnergy>();
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);
395 /** @brief Returns the total energy consumed by the link so far (in Joules)
397 * See also @ref SURF_plugin_energy.
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();