Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
rename callback cpuActionStateChangedCallbacks into CpuAction::onStateChange
[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     simgrid::surf::VMCreatedCallbacks.connect(energyVMCreatedCallback);
146     simgrid::surf::Host::onCreation.connect(energyHostCreatedCallback);
147     simgrid::surf::Host::onDestruction.connect(energyHostDestructedCallback);
148     simgrid::surf::CpuAction::onStateChange.connect(
149       energyCpuActionStateChangedCallback);
150     simgrid::surf::surfExitCallbacks.connect(sg_energy_plugin_exit);
151     simgrid::surf::Host::onStateChange.connect(
152       energyStateChangedCallback);
153   }
154 }
155
156 namespace simgrid {
157 namespace energy {
158
159 /**
160  *
161  */
162 HostEnergy::HostEnergy(simgrid::surf::Host *ptr)
163 {
164   host = ptr;
165   total_energy = 0;
166   power_range_watts_list = getWattsRangeList();
167   last_updated = surf_get_clock();
168
169   if (host->getProperties() != NULL) {
170         char* off_power_str = (char*)xbt_dict_get_or_null(host->getProperties(), "watt_off");
171         if (off_power_str != NULL)
172                 watts_off = atof(off_power_str);
173         else
174                 watts_off = 0;
175   }
176
177 }
178
179 HostEnergy::~HostEnergy(){
180   unsigned int iter;
181   xbt_dynar_t power_tuple = NULL;
182   xbt_dynar_foreach(power_range_watts_list, iter, power_tuple)
183     xbt_dynar_free(&power_tuple);
184   xbt_dynar_free(&power_range_watts_list);
185 }
186
187
188 double HostEnergy::getWattMinAt(int pstate) {
189   xbt_dynar_t power_range_list = power_range_watts_list;
190   xbt_assert(power_range_watts_list, "No power range properties specified for host %s", host->getName());
191   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);
192   double min_power = xbt_dynar_get_as(current_power_values, 0, double);
193   return min_power;
194 }
195 double HostEnergy::getWattMaxAt(int pstate) {
196   xbt_dynar_t power_range_list = power_range_watts_list;
197   xbt_assert(power_range_watts_list, "No power range properties specified for host %s", host->getName());
198   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);
199   double max_power = xbt_dynar_get_as(current_power_values, 1, double);
200   return max_power;
201 }
202
203 /**
204  * Computes the power consumed by the host according to the current pstate and processor load
205  *
206  */
207 double HostEnergy::getCurrentWattsValue(double cpu_load)
208 {
209         xbt_dynar_t power_range_list = power_range_watts_list;
210         xbt_assert(power_range_watts_list, "No power range properties specified for host %s", host->getName());
211
212     /* retrieve the power values associated with the current pstate */
213     xbt_dynar_t current_power_values = xbt_dynar_get_as( power_range_list,
214       static_cast<simgrid::surf::CpuCas01*>(host->p_cpu)->getPState(),
215       xbt_dynar_t);
216
217     /* min_power corresponds to the idle power (cpu load = 0) */
218     /* max_power is the power consumed at 100% cpu load       */
219     double min_power = xbt_dynar_get_as(current_power_values, 0, double);
220     double max_power = xbt_dynar_get_as(current_power_values, 1, double);
221     double power_slope = max_power - min_power;
222
223     double current_power = min_power + cpu_load * power_slope;
224
225         XBT_DEBUG("[get_current_watts] min_power=%f, max_power=%f, slope=%f", min_power, max_power, power_slope);
226     XBT_DEBUG("[get_current_watts] Current power (watts) = %f, load = %f", current_power, cpu_load);
227
228         return current_power;
229 }
230
231 double HostEnergy::getConsumedEnergy()
232 {
233
234         if(last_updated < surf_get_clock())
235                 update_consumption(host, this);
236         return total_energy;
237
238 }
239
240 xbt_dynar_t HostEnergy::getWattsRangeList()
241 {
242         xbt_dynar_t power_range_list;
243         xbt_dynar_t power_tuple;
244         int i = 0, pstate_nb=0;
245         xbt_dynar_t current_power_values;
246         double min_power, max_power;
247
248         if (host->getProperties() == NULL)
249                 return NULL;
250
251         char* all_power_values_str = (char*)xbt_dict_get_or_null(host->getProperties(), "watt_per_state");
252
253         if (all_power_values_str == NULL)
254                 return NULL;
255
256
257         power_range_list = xbt_dynar_new(sizeof(xbt_dynar_t), NULL);
258         xbt_dynar_t all_power_values = xbt_str_split(all_power_values_str, ",");
259
260         pstate_nb = xbt_dynar_length(all_power_values);
261         for (i=0; i< pstate_nb; i++)
262         {
263                 /* retrieve the power values associated with the current pstate */
264                 current_power_values = xbt_str_split(xbt_dynar_get_as(all_power_values, i, char*), ":");
265                 xbt_assert(xbt_dynar_length(current_power_values) > 1,
266                                 "Power properties incorrectly defined - could not retrieve min and max power values for host %s",
267                                 host->getName());
268
269                 /* min_power corresponds to the idle power (cpu load = 0) */
270                 /* max_power is the power consumed at 100% cpu load       */
271                 min_power = atof(xbt_dynar_get_as(current_power_values, 0, char*));
272                 max_power = atof(xbt_dynar_get_as(current_power_values, 1, char*));
273
274                 power_tuple = xbt_dynar_new(sizeof(double), NULL);
275                 xbt_dynar_push_as(power_tuple, double, min_power);
276                 xbt_dynar_push_as(power_tuple, double, max_power);
277
278                 xbt_dynar_push_as(power_range_list, xbt_dynar_t, power_tuple);
279                 xbt_dynar_free(&current_power_values);
280         }
281         xbt_dynar_free(&all_power_values);
282         return power_range_list;
283 }
284
285 }
286 }