Logo AND Algorithmique Numérique Distribuée

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