Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[surf] Triggers the destructed callbacks on the full object
[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 <src/surf/plugins/energy.hpp>
8 #include <src/surf/cpu_cas01.hpp>
9 #include <src/surf/virtual_machine.hpp>
10
11 /** @addtogroup SURF_plugin_energy
12
13
14 This is the energy plugin, enabling to account not only for computation time,
15 but also for the dissipated energy in the simulated platform.
16
17 The energy consumption of a CPU depends directly of its current load. Specify that consumption in your platform file as follows:
18
19 \verbatim
20 <host id="HostA" power="100.0Mf" >
21     <prop id="watt_per_state" value="100.0:200.0" />
22     <prop id="watt_off" value="10" />
23 </host>
24 \endverbatim
25
26 The first property means that when your host is up and running, but without anything to do, it will dissipate 100 Watts.
27 If it's fully loaded, it will dissipate 200 Watts. If its load is at 50%, then it will dissipate 150 Watts.
28 The second property means that when your host is turned off, it will dissipate only 10 Watts (please note that these values are arbitrary).
29
30 If your CPU is using pstates, then you can provide one consumption interval per pstate.
31
32 \verbatim
33 <host id="HostB" power="100.0Mf,50.0Mf,20.0Mf" pstate="0" >
34     <prop id="watt_per_state" value="95.0:200.0, 93.0:170.0, 90.0:150.0" />
35     <prop id="watt_off" value="10" />
36 </host>
37 \endverbatim
38
39 That host has 3 levels of performance with the following performance: 100 Mflop/s, 50 Mflop/s or 20 Mflop/s.
40 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.
41 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,
42 170 Watts and 150 Watts respectively.
43
44 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().
45
46 To simulate the energy-related elements, first call the #sg_energy_plugin_init() before your #MSG_init(),
47 and then use the following function to retrieve the consumption of a given host: #MSG_host_get_consumed_energy().
48  */
49
50 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_energy, surf,
51                                 "Logging specific to the SURF energy plugin");
52
53 namespace simgrid {
54 namespace energy {
55
56 std::map<simgrid::surf::Host*, HostEnergy*> *surf_energy = NULL;
57
58 }
59 }
60
61 using simgrid::energy::HostEnergy;
62 using simgrid::energy::surf_energy;
63
64 static void energyHostCreatedCallback(simgrid::surf::Host *host){
65   (*surf_energy)[host] = new HostEnergy(host);
66 }
67
68 static void energyVMCreatedCallback(simgrid::surf::VirtualMachine* vm) {
69   std::map<simgrid::surf::Host*, HostEnergy*>::iterator host_energy_it = surf_energy->find(vm->p_subWs);
70   xbt_assert(host_energy_it != surf_energy->end(), "The host is not in surf_energy.");
71   (*surf_energy)[vm] = host_energy_it->second;
72   host_energy_it->second->ref(); // protect the HostEnergy from getting deleted too early
73 }
74
75 /* Computes the consumption so far.  Called lazily on need. */
76 static void update_consumption(simgrid::surf::Host *host, HostEnergy *host_energy) {
77         double cpu_load = lmm_constraint_get_usage(host->p_cpu->getConstraint()) / host->p_cpu->m_speedPeak;
78         double start_time = host_energy->last_updated;
79         double finish_time = surf_get_clock();
80
81         double previous_energy = host_energy->total_energy;
82
83         double instantaneous_consumption;
84         if (host->getState() == SURF_RESOURCE_OFF)
85                 instantaneous_consumption = host_energy->watts_off;
86         else
87                 instantaneous_consumption = host_energy->getCurrentWattsValue(cpu_load);
88
89         double energy_this_step = instantaneous_consumption*(finish_time-start_time);
90
91         host_energy->total_energy = previous_energy + energy_this_step;
92         host_energy->last_updated = finish_time;
93
94         XBT_DEBUG("[cpu_update_energy] period=[%.2f-%.2f]; current power peak=%.0E flop/s; consumption change: %.2f J -> %.2f J",
95                   start_time, finish_time, host->p_cpu->m_speedPeak, previous_energy, energy_this_step);
96 }
97
98 static void energyHostDestructedCallback(simgrid::surf::Host *host){
99   std::map<simgrid::surf::Host*, HostEnergy*>::iterator host_energy_it = surf_energy->find(host);
100   xbt_assert(host_energy_it != surf_energy->end(), "The host is not in surf_energy.");
101
102   HostEnergy *host_energy = host_energy_it->second;
103   update_consumption(host, host_energy);
104
105   if (host_energy_it->second->refcount == 1) // Don't display anything for virtual CPUs
106           XBT_INFO("Total energy of host %s: %f Joules", host->getName(), host_energy->getConsumedEnergy());
107   host_energy_it->second->unref();
108   surf_energy->erase(host_energy_it);
109 }
110
111 static void energyCpuActionStateChangedCallback(
112     simgrid::surf::CpuAction *action, e_surf_action_state_t old, e_surf_action_state_t cur)
113 {
114   const char *name = getActionCpu(action)->getName();
115   simgrid::surf::Host *host = static_cast<simgrid::surf::Host*>(surf_host_resource_priv(sg_host_by_name(name)));
116
117   HostEnergy *host_energy = (*surf_energy)[host];
118
119   if(host_energy->last_updated < surf_get_clock())
120           update_consumption(host, host_energy);
121 }
122
123 static void energyStateChangedCallback(simgrid::surf::Host *host, e_surf_resource_state_t oldState, e_surf_resource_state_t newState)
124 {
125   HostEnergy *host_energy = (*surf_energy)[host];
126
127   if(host_energy->last_updated < surf_get_clock())
128           update_consumption(host, host_energy);
129 }
130
131 static void sg_energy_plugin_exit()
132 {
133   delete surf_energy;
134   surf_energy = NULL;
135 }
136
137 /** \ingroup SURF_plugin_energy
138  * \brief Enable energy plugin
139  * \details Enable energy plugin to get joules consumption of each cpu. You should call this function before #MSG_init().
140  */
141 void sg_energy_plugin_init() {
142   if (simgrid::energy::surf_energy == NULL) {
143     simgrid::energy::surf_energy =
144       new std::map<simgrid::surf::Host*, simgrid::energy::HostEnergy*>();
145     surf_callback_connect(simgrid::surf::hostCreatedCallbacks, energyHostCreatedCallback);
146     surf_callback_connect(simgrid::surf::VMCreatedCallbacks, energyVMCreatedCallback);
147     surf_callback_connect(simgrid::surf::hostDestructedCallbacks, energyHostDestructedCallback);
148     surf_callback_connect(simgrid::surf::cpuActionStateChangedCallbacks, energyCpuActionStateChangedCallback);
149     surf_callback_connect(simgrid::surf::surfExitCallbacks, sg_energy_plugin_exit);
150     surf_callback_connect(simgrid::surf::hostStateChangedCallbacks, energyStateChangedCallback);
151   }
152 }
153
154 namespace simgrid {
155 namespace energy {
156
157 /**
158  *
159  */
160 HostEnergy::HostEnergy(simgrid::surf::Host *ptr)
161 {
162   host = ptr;
163   total_energy = 0;
164   power_range_watts_list = getWattsRangeList();
165   last_updated = surf_get_clock();
166
167   if (host->getProperties() != NULL) {
168         char* off_power_str = (char*)xbt_dict_get_or_null(host->getProperties(), "watt_off");
169         if (off_power_str != NULL)
170                 watts_off = atof(off_power_str);
171         else
172                 watts_off = 0;
173   }
174
175 }
176
177 HostEnergy::~HostEnergy(){
178   unsigned int iter;
179   xbt_dynar_t power_tuple = NULL;
180   xbt_dynar_foreach(power_range_watts_list, iter, power_tuple)
181     xbt_dynar_free(&power_tuple);
182   xbt_dynar_free(&power_range_watts_list);
183 }
184
185
186 double HostEnergy::getWattMinAt(int pstate) {
187   xbt_dynar_t power_range_list = power_range_watts_list;
188   xbt_assert(power_range_watts_list, "No power range properties specified for host %s", host->getName());
189   xbt_dynar_t current_power_values = xbt_dynar_get_as(power_range_list, static_cast<simgrid::surf::CpuCas01*>(host->p_cpu)->getPState(), xbt_dynar_t);
190   double min_power = xbt_dynar_get_as(current_power_values, 0, double);
191   return min_power;
192 }
193 double HostEnergy::getWattMaxAt(int pstate) {
194   xbt_dynar_t power_range_list = power_range_watts_list;
195   xbt_assert(power_range_watts_list, "No power range properties specified for host %s", host->getName());
196   xbt_dynar_t current_power_values = xbt_dynar_get_as(power_range_list, static_cast<simgrid::surf::CpuCas01*>(host->p_cpu)->getPState(), xbt_dynar_t);
197   double max_power = xbt_dynar_get_as(current_power_values, 1, double);
198   return max_power;
199 }
200
201 /**
202  * Computes the power consumed by the host according to the current pstate and processor load
203  *
204  */
205 double HostEnergy::getCurrentWattsValue(double cpu_load)
206 {
207         xbt_dynar_t power_range_list = power_range_watts_list;
208         xbt_assert(power_range_watts_list, "No power range properties specified for host %s", host->getName());
209
210     /* retrieve the power values associated with the current pstate */
211     xbt_dynar_t current_power_values = xbt_dynar_get_as( power_range_list,
212       static_cast<simgrid::surf::CpuCas01*>(host->p_cpu)->getPState(),
213       xbt_dynar_t);
214
215     /* min_power corresponds to the idle power (cpu load = 0) */
216     /* max_power is the power consumed at 100% cpu load       */
217     double min_power = xbt_dynar_get_as(current_power_values, 0, double);
218     double max_power = xbt_dynar_get_as(current_power_values, 1, double);
219     double power_slope = max_power - min_power;
220
221     double current_power = min_power + cpu_load * power_slope;
222
223         XBT_DEBUG("[get_current_watts] min_power=%f, max_power=%f, slope=%f", min_power, max_power, power_slope);
224     XBT_DEBUG("[get_current_watts] Current power (watts) = %f, load = %f", current_power, cpu_load);
225
226         return current_power;
227 }
228
229 double HostEnergy::getConsumedEnergy()
230 {
231
232         if(last_updated < surf_get_clock())
233                 update_consumption(host, this);
234         return total_energy;
235
236 }
237
238 xbt_dynar_t HostEnergy::getWattsRangeList()
239 {
240         xbt_dynar_t power_range_list;
241         xbt_dynar_t power_tuple;
242         int i = 0, pstate_nb=0;
243         xbt_dynar_t current_power_values;
244         double min_power, max_power;
245
246         if (host->getProperties() == NULL)
247                 return NULL;
248
249         char* all_power_values_str = (char*)xbt_dict_get_or_null(host->getProperties(), "watt_per_state");
250
251         if (all_power_values_str == NULL)
252                 return NULL;
253
254
255         power_range_list = xbt_dynar_new(sizeof(xbt_dynar_t), NULL);
256         xbt_dynar_t all_power_values = xbt_str_split(all_power_values_str, ",");
257
258         pstate_nb = xbt_dynar_length(all_power_values);
259         for (i=0; i< pstate_nb; i++)
260         {
261                 /* retrieve the power values associated with the current pstate */
262                 current_power_values = xbt_str_split(xbt_dynar_get_as(all_power_values, i, char*), ":");
263                 xbt_assert(xbt_dynar_length(current_power_values) > 1,
264                                 "Power properties incorrectly defined - could not retrieve min and max power values for host %s",
265                                 host->getName());
266
267                 /* min_power corresponds to the idle power (cpu load = 0) */
268                 /* max_power is the power consumed at 100% cpu load       */
269                 min_power = atof(xbt_dynar_get_as(current_power_values, 0, char*));
270                 max_power = atof(xbt_dynar_get_as(current_power_values, 1, char*));
271
272                 power_tuple = xbt_dynar_new(sizeof(double), NULL);
273                 xbt_dynar_push_as(power_tuple, double, min_power);
274                 xbt_dynar_push_as(power_tuple, double, max_power);
275
276                 xbt_dynar_push_as(power_range_list, xbt_dynar_t, power_tuple);
277                 xbt_dynar_free(&current_power_values);
278         }
279         xbt_dynar_free(&all_power_values);
280         return power_range_list;
281 }
282
283 }
284 }