Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[energy] C++-ification, remove nested xbt_dynar
[simgrid.git] / src / surf / plugins / energy.cpp
1 /* Copyright (c) 2010, 2012-2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <utility>
8 #include <vector>
9
10 #include <simgrid/plugins/energy.h>
11 #include <simgrid/simix.hpp>
12 #include <src/surf/plugins/energy.hpp>
13 #include <src/surf/cpu_interface.hpp>
14 #include <src/surf/virtual_machine.hpp>
15
16 /** @addtogroup SURF_plugin_energy
17
18
19 This is the energy plugin, enabling to account not only for computation time,
20 but also for the dissipated energy in the simulated platform.
21
22 The energy consumption of a CPU depends directly of its current load. Specify that consumption in your platform file as follows:
23
24 \verbatim
25 <host id="HostA" power="100.0Mf" >
26     <prop id="watt_per_state" value="100.0:200.0" />
27     <prop id="watt_off" value="10" />
28 </host>
29 \endverbatim
30
31 The first property means that when your host is up and running, 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 values are arbitrary).
34
35 If your CPU is using pstates, then you can provide one consumption interval per pstate.
36
37 \verbatim
38 <host id="HostB" power="100.0Mf,50.0Mf,20.0Mf" pstate="0" >
39     <prop id="watt_per_state" value="95.0:200.0, 93.0:170.0, 90.0:150.0" />
40     <prop id="watt_off" value="10" />
41 </host>
42 \endverbatim
43
44 That host has 3 levels of performance with the following performance: 100 Mflop/s, 50 Mflop/s or 20 Mflop/s.
45 It starts at pstate 0 (ie, at 100 Mflop/s). In this case, you have to specify one interval per pstate in the watt_per_state property.
46 In this example, the idle consumption is 95 Watts, 93 Watts and 90 Watts in each pstate while the CPU burn consumption are at 200 Watts,
47 170 Watts and 150 Watts respectively.
48
49 To change the pstate of a given CPU, use the following functions: #MSG_host_get_nb_pstates(), #MSG_host_set_pstate(), #MSG_host_get_power_peak_at().
50
51 To simulate the energy-related elements, first call the #sg_energy_plugin_init() before your #MSG_init(),
52 and then use the following function to retrieve the consumption of a given host: #MSG_host_get_consumed_energy().
53  */
54
55 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf,
56                                 "Logging specific to the SURF energy plugin");
57
58 std::map<simgrid::surf::Host*, simgrid::energy::HostEnergy*> *simgrid::energy::surf_energy = NULL;
59
60 using simgrid::energy::HostEnergy;
61
62 /* Computes the consumption so far.  Called lazily on need. */
63 static void update_consumption(simgrid::surf::Host *host, HostEnergy *host_energy) {
64         double start_time = host_energy->last_updated;
65         double finish_time = surf_get_clock();
66         double cpu_load;
67         if (host->p_cpu->m_speedPeak == 0)
68                 // Some users declare a pstate of speed 0 flops (eg to model boot time).
69                 // We consider that the machine is then fully loaded. That's arbitrary but it avoids a NaN
70                 cpu_load = 1;
71         else
72                 cpu_load = lmm_constraint_get_usage(host->p_cpu->getConstraint()) / host->p_cpu->m_speedPeak;
73
74         if (cpu_load > 1) // A machine with a load > 1 consumes as much as a fully loaded machine, not mores
75           cpu_load = 1;
76
77         double previous_energy = host_energy->total_energy;
78
79         double instantaneous_consumption;
80         if (host->isOff())
81                 instantaneous_consumption = host_energy->watts_off;
82         else
83                 instantaneous_consumption = host_energy->getCurrentWattsValue(cpu_load);
84
85         double energy_this_step = instantaneous_consumption*(finish_time-start_time);
86
87         host_energy->total_energy = previous_energy + energy_this_step;
88         host_energy->last_updated = finish_time;
89
90         XBT_DEBUG("[update_energy of %s] period=[%.2f-%.2f]; current power peak=%.0E flop/s; consumption change: %.2f J -> %.2f J",
91             host->getName(), start_time, finish_time, host->p_cpu->m_speedPeak, previous_energy, energy_this_step);
92 }
93
94 namespace simgrid {
95 namespace energy {
96
97 HostEnergy::HostEnergy(simgrid::surf::Host *ptr) :
98   host(ptr), last_updated(surf_get_clock())
99 {
100   initWattsRangeList();
101
102   if (host->getProperties() != NULL) {
103     char* off_power_str = (char*)xbt_dict_get_or_null(host->getProperties(), "watt_off");
104     if (off_power_str != NULL)
105       watts_off = atof(off_power_str);
106     else
107       watts_off = 0;
108   }
109
110 }
111
112 HostEnergy::~HostEnergy()
113 {
114 }
115
116 double HostEnergy::getWattMinAt(int pstate)
117 {
118   xbt_assert(!power_range_watts_list.empty(),
119     "No power range properties specified for host %s", host->getName());
120   return power_range_watts_list[pstate].first;
121 }
122
123 double HostEnergy::getWattMaxAt(int pstate)
124 {
125   xbt_assert(!power_range_watts_list.empty(),
126     "No power range properties specified for host %s", host->getName());
127   return power_range_watts_list[pstate].second;
128 }
129
130 /** @brief Computes the power consumed by the host according to the current pstate and processor load */
131 double HostEnergy::getCurrentWattsValue(double cpu_load)
132 {
133         xbt_assert(!power_range_watts_list.empty(),
134     "No power range properties specified for host %s", host->getName());
135
136   /* min_power corresponds to the idle power (cpu load = 0) */
137   /* max_power is the power consumed at 100% cpu load       */
138   auto range = power_range_watts_list.at(host->p_cpu->getPState());
139   double min_power = range.first;
140   double max_power = range.second;
141   double power_slope = max_power - min_power;
142         double current_power = min_power + cpu_load * power_slope;
143
144         XBT_DEBUG("[get_current_watts] min_power=%f, max_power=%f, slope=%f", min_power, max_power, power_slope);
145         XBT_DEBUG("[get_current_watts] Current power (watts) = %f, load = %f", current_power, cpu_load);
146
147         return current_power;
148 }
149
150 double HostEnergy::getConsumedEnergy()
151 {
152         if(last_updated < surf_get_clock())
153                 update_consumption(host, this);
154         return total_energy;
155 }
156
157 void HostEnergy::initWattsRangeList()
158 {
159         if (host->getProperties() == NULL)
160                 return;
161         char* all_power_values_str =
162     (char*)xbt_dict_get_or_null(host->getProperties(), "watt_per_state");
163         if (all_power_values_str == NULL)
164                 return;
165
166         xbt_dynar_t all_power_values = xbt_str_split(all_power_values_str, ",");
167         int pstate_nb = xbt_dynar_length(all_power_values);
168
169         for (int i=0; i< pstate_nb; i++)
170         {
171                 /* retrieve the power values associated with the current pstate */
172                 xbt_dynar_t current_power_values = xbt_str_split(xbt_dynar_get_as(all_power_values, i, char*), ":");
173                 xbt_assert(xbt_dynar_length(current_power_values) > 1,
174                                 "Power properties incorrectly defined - could not retrieve min and max power values for host %s",
175                                 host->getName());
176
177                 /* min_power corresponds to the idle power (cpu load = 0) */
178                 /* max_power is the power consumed at 100% cpu load       */
179     power_range_watts_list.push_back(power_range(
180       atof(xbt_dynar_get_as(current_power_values, 0, char*)),
181       atof(xbt_dynar_get_as(current_power_values, 1, char*))
182     ));
183         }
184         xbt_dynar_free(&all_power_values);
185 }
186
187
188 double surf_host_get_wattmin_at(sg_host_t host, int pstate){
189   xbt_assert(surf_energy!=NULL, "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
190   std::map<simgrid::surf::Host*, HostEnergy*>::iterator hostIt = surf_energy->find(host->extension<simgrid::surf::Host>());
191   return hostIt->second->getWattMinAt(pstate);
192 }
193 double surf_host_get_wattmax_at(sg_host_t host, int pstate){
194   xbt_assert(surf_energy!=NULL, "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
195   std::map<simgrid::surf::Host*, HostEnergy*>::iterator hostIt = surf_energy->find(host->extension<simgrid::surf::Host>());
196   return hostIt->second->getWattMaxAt(pstate);
197 }
198
199 double surf_host_get_consumed_energy(sg_host_t host){
200   xbt_assert(surf_energy!=NULL, "The Energy plugin is not active. Please call sg_energy_plugin_init() during initialization.");
201   std::map<simgrid::surf::Host*, HostEnergy*>::iterator hostIt = surf_energy->find(host->extension<simgrid::surf::Host>());
202   return hostIt->second->getConsumedEnergy();
203 }
204
205 }
206 }
207
208 /* **************************** Public interface *************************** */
209
210 /** \ingroup SURF_plugin_energy
211  * \brief Enable energy plugin
212  * \details Enable energy plugin to get joules consumption of each cpu. You should call this function before #MSG_init().
213  */
214 void sg_energy_plugin_init() {
215   if (simgrid::energy::surf_energy == NULL) {
216
217   simgrid::energy::surf_energy = new std::map<simgrid::surf::Host*, simgrid::energy::HostEnergy*>();
218
219   /* The following attaches an anonymous function to the Host::onCreation signal */
220   /* Search for "C++ lambda" for more information on the syntax used here */
221   simgrid::surf::Host::onCreation.connect([](simgrid::surf::Host *host) {
222     if (dynamic_cast<simgrid::surf::VirtualMachine*>(host)) // Ignore virtual machines
223       return;
224
225     (*simgrid::energy::surf_energy)[host] = new HostEnergy(host);
226   });
227
228   simgrid::surf::Host::onDestruction.connect([](simgrid::surf::Host *host) {
229     if (dynamic_cast<simgrid::surf::VirtualMachine*>(host)) // Ignore virtual machines
230       return;
231
232     std::map<simgrid::surf::Host*, HostEnergy*>::iterator host_energy_it = simgrid::energy::surf_energy->find(host);
233     xbt_assert(host_energy_it != simgrid::energy::surf_energy->end(), "The host is not in surf_energy.");
234
235     HostEnergy *host_energy = host_energy_it->second;
236     update_consumption(host, host_energy);
237
238     XBT_INFO("Total energy of host %s: %f Joules", host->getName(), host_energy->getConsumedEnergy());
239     host_energy_it->second->unref();
240     simgrid::energy::surf_energy->erase(host_energy_it);
241   });
242
243   simgrid::surf::CpuAction::onStateChange.connect([](simgrid::surf::CpuAction *action,
244       e_surf_action_state_t old,
245       e_surf_action_state_t cur) {
246     const char *name = getActionCpu(action)->getName();
247     simgrid::surf::Host *host = sg_host_by_name(name)->extension<simgrid::surf::Host>();
248     simgrid::surf::VirtualMachine *vm = dynamic_cast<simgrid::surf::VirtualMachine*>(host);
249     if (vm) // If it's a VM, take the corresponding PM
250         host = vm->getPm()->extension<simgrid::surf::Host>();
251
252     HostEnergy *host_energy = (*simgrid::energy::surf_energy)[host];
253
254     if(host_energy->last_updated < surf_get_clock())
255       update_consumption(host, host_energy);
256
257   });
258
259   simgrid::surf::Host::onStateChange.connect([] (simgrid::surf::Host *host) {
260     if (dynamic_cast<simgrid::surf::VirtualMachine*>(host)) // Ignore virtual machines
261       return;
262
263     HostEnergy *host_energy = (*simgrid::energy::surf_energy)[host];
264
265     if(host_energy->last_updated < surf_get_clock())
266       update_consumption(host, host_energy);
267   });
268
269   simgrid::surf::surfExitCallbacks.connect([]() {
270     delete simgrid::energy::surf_energy;
271     simgrid::energy::surf_energy = NULL;
272   });
273   }
274 }
275
276 /** @brief Returns the total energy consumed by the host so far (in Joules)
277  *
278  *  See also @ref SURF_plugin_energy.
279  */
280 double sg_host_get_consumed_energy(sg_host_t host) {
281   return simgrid::energy::surf_host_get_consumed_energy(host);
282 }
283
284 /** @brief Get the amount of watt dissipated at the given pstate when the host is idling */
285 double sg_host_get_wattmin_at(sg_host_t host, int pstate) {
286   return simgrid::simix::kernel(std::bind(
287       simgrid::energy::surf_host_get_wattmin_at, host, pstate
288   ));
289 }
290 /** @brief  Returns the amount of watt dissipated at the given pstate when the host burns CPU at 100% */
291 double sg_host_get_wattmax_at(sg_host_t host, int pstate) {
292   return simgrid::simix::kernel(std::bind(
293       simgrid::energy::surf_host_get_wattmax_at, host, pstate
294   ));
295 }