Logo AND Algorithmique Numérique Distribuée

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