Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'dev-profile-callbacks' into 'master'
[simgrid.git] / src / kernel / resource / profile / ProfileBuilder.cpp
1 /* Copyright (c) 2004-2022. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "simgrid/kernel/ProfileBuilder.hpp"
7 #include "simgrid/forward.h"
8 #include "src/kernel/resource/profile/Profile.hpp"
9 #include "src/kernel/resource/profile/StochasticDatedValue.hpp"
10 #include "src/surf/surf_interface.hpp"
11
12 #include <boost/algorithm/string.hpp>
13 #include <boost/intrusive/options.hpp>
14 #include <cstddef>
15 #include <fstream>
16 #include <sstream>
17
18
19 namespace simgrid {
20 namespace kernel {
21 namespace profile {
22
23 bool DatedValue::operator==(DatedValue const& e2) const
24 {
25   return (fabs(date_ - e2.date_) < 0.0001) && (fabs(value_ - e2.value_) < 0.0001);
26 }
27
28 std::ostream& operator << (std::ostream& out, const DatedValue& dv) {
29   out<<"(+"<<dv.date_<<','<<dv.value_<<')';
30   return out;
31 }
32
33 /** @brief This callback implements the support for legacy profile syntax
34 *
35 * These profiles support two orthogonal concepts:
36 * - One-shot/Loop: the given list of {date,value} pairs can be repeated forever or just once. 
37 * - Deterministic/Stochastic: dates and/or values can be drawn according to a stochastic law.  
38 *
39 * A legacy profile is composed of "global" instructions and "local" instructions.
40 *
41 * Global instructions set properties that are true for all {date,value} pairs. They include
42 * - STOCHASTIC -> declare that the profile will use stochastic {date,value} pairs and not loop.
43 * - STOCHASTIC_LOOP -> declare that the profile will use stochastic {date,value} pairs and loop.
44 * - PERIODICITY -> declare the period for each iteration of the profile pattern.
45 * - LOOP_AFTER -> declare that the profile pattern will start over after a fixed delay
46 *
47 * Note that PERIODICITY and LOOP_AFTER have slightly different meanings. 
48 * PERIODICITY represents the delay between two iterations, for instance the time for the first pair of the pattern in two successive iterations.
49 * Hence, PERIODICITY only has meaning for completely deterministic profiles.
50 * LOOP_AFTER represents an additional delay between the last pair of a profile pattern iteration and the first pair of the next iteration.
51 *  
52 * Local instructions define one {date,value} pair, or more exactly one pattern. 
53 * In effect, when a profile loops or has stochastic components, the actual {date,value} pairs will be generated dynamically.    
54 * Roughly, a local instruction is a line with the following syntax:
55 * <date spec> <value spec>
56
57 * Both date and value specifications may have one of the following formats:
58 * - <num> : in completely deterministic profiles, use this number
59 * - DET <num> : in stochastic profiles, use this number
60 * - NORM <mean> <standard deviation> : draw number from a normal distribution of given parameters   
61 * - UNIF <min> <max> : draw number from an uniform distribution of given parameters   
62 * - EXP <lambda> : draw number from an exponential distribution of given parameters   
63 *
64 * Note that in stochastic profiles, the date component is (necessarily) representing the delay between two pairs; 
65 * whereas in deterministic profiles, the date components represent the absolute date at which the elements are used in the first iteration.  
66 */
67
68 class LegacyUpdateCb {
69
70 std::vector<StochasticDatedValue> pattern;
71
72 bool stochastic;
73
74 bool loop;
75
76 double loop_delay;
77
78 static bool is_comment_or_empty_line(const std::string& val)
79 {
80   return (val[0] == '#' || val[0] == '\0' || val[0] == '%');
81 }
82
83 static bool is_normal_distribution(const std::string& val)
84 {
85   return (val == "NORM" || val == "NORMAL" || val == "GAUSS" || val == "GAUSSIAN");
86 }
87
88 static bool is_exponential_distribution(const std::string& val)
89 {
90   return (val == "EXP" || val == "EXPONENTIAL");
91 }
92
93 static bool is_uniform_distribution(const std::string& val)
94 {
95   return (val == "UNIF" || val == "UNIFORM");
96 }
97
98 public: 
99   LegacyUpdateCb(const std::string& input, double periodicity): stochastic(false),loop(periodicity>0),loop_delay(0)
100   {
101     int linecount                                    = 0;
102     std::vector<std::string> list;
103     double last_date=0;
104     boost::split(list, input, boost::is_any_of("\n\r"));
105     for (auto val : list) {
106       simgrid::kernel::profile::DatedValue event;
107       simgrid::kernel::profile::StochasticDatedValue stochevent;
108       linecount++;
109       boost::trim(val);
110       if (is_comment_or_empty_line(val))
111         continue;
112       if (sscanf(val.c_str(), "PERIODICITY %lg\n", &periodicity) == 1) {
113         loop=true;
114         continue;
115       }
116       if (sscanf(val.c_str(), "LOOPAFTER %lg\n", &loop_delay) == 1) {
117         loop=true;
118         continue;
119       }
120       if (val == "STOCHASTIC LOOP") {
121         stochastic=true;
122         loop=true;
123         continue;
124       }
125       if (val == "STOCHASTIC") {
126         stochastic=true;
127         continue;
128       }
129
130       unsigned int i;
131       unsigned int j;
132       std::istringstream iss(val);
133       std::vector<std::string> splittedval((std::istream_iterator<std::string>(iss)),
134                                             std::istream_iterator<std::string>());
135
136       xbt_assert(not splittedval.empty(), "Invalid profile line");
137
138       if (splittedval[0] == "DET") {
139         stochevent.date_law = Distribution::DET;
140         i                   = 2;
141       } else if (is_normal_distribution(splittedval[0])) {
142         stochevent.date_law = Distribution::NORM;
143         i                   = 3;
144       } else if (is_exponential_distribution(splittedval[0])) {
145         stochevent.date_law = Distribution::EXP;
146         i                   = 2;
147       } else if (is_uniform_distribution(splittedval[0])) {
148         stochevent.date_law = Distribution::UNIF;
149         i                   = 3;
150       } else {
151         xbt_assert(not stochastic);
152         stochevent.date_law = Distribution::DET;
153         i                   = 1;
154       }
155
156       xbt_assert(splittedval.size() > i, "Invalid profile line");
157       if (i == 1 or i == 2) {
158         stochevent.date_params = {std::stod(splittedval[i-1])};
159       } else if (i == 3) {
160         stochevent.date_params = {std::stod(splittedval[1]), std::stod(splittedval[2])};
161       }
162
163       if (splittedval[i] == "DET") {
164         stochevent.value_law = Distribution::DET;
165         j                    = 1;
166       } else if (is_normal_distribution(splittedval[i])) {
167         stochevent.value_law = Distribution::NORM;
168         j                    = 2;
169       } else if (is_exponential_distribution(splittedval[i])) {
170         stochevent.value_law = Distribution::EXP;
171         j                    = 1;
172       } else if (is_uniform_distribution(splittedval[i])) {
173         stochevent.value_law = Distribution::UNIF;
174         j                    = 2;
175       } else {
176         xbt_assert(not stochastic);
177         stochevent.value_law = Distribution::DET;
178         j                   = 0;
179       }
180
181       xbt_assert(splittedval.size() > i + j, "Invalid profile line");
182       if (j == 0 or j == 1) {
183         stochevent.value_params = {std::stod(splittedval[i + j])};
184       } else if (j == 2) {
185         stochevent.value_params = {std::stod(splittedval[i + 1]), std::stod(splittedval[i + 2])};
186       }
187
188       if(not stochastic) {
189         //In this mode, dates read from the string are absolute values
190         double new_date=stochevent.date_params[0];
191         xbt_assert(new_date>=0,"Profile time value is negative, why?");
192         xbt_assert( last_date <= new_date ,
193                     "%d: Invalid trace: Events must be sorted, but time %g > time %g.\n%s", linecount,
194                     last_date, new_date, input.c_str());
195         stochevent.date_params[0]-=last_date;
196         last_date=new_date;
197       }
198
199       pattern.emplace_back(stochevent);
200     }
201
202     xbt_assert(not stochastic or periodicity <= 0, "If you want periodicity with stochastic profiles, use LOOP_AFTER");
203     if(periodicity>0) {
204       xbt_assert(loop and loop_delay==0);
205       loop_delay=periodicity - last_date;
206     }
207
208     xbt_assert(loop_delay>=0,"Profile loop conditions are not realizable!");
209
210   }
211
212   double get_repeat_delay() {
213     if(not stochastic and loop)
214       return loop_delay;
215     return -1.0;
216   }
217
218   void operator() (std::vector<DatedValue>& event_list) {
219     size_t initial_size=event_list.size();
220     if( loop or not initial_size ){
221       for(auto dv : pattern)
222         event_list.emplace_back(dv.get_datedvalue());
223       if(initial_size)
224         event_list.at(initial_size).date_+=loop_delay;
225     }
226   }
227
228
229 };
230
231
232 Profile* ProfileBuilder::from_string(const std::string& name, const std::string& input, double periodicity)
233 {
234   LegacyUpdateCb cb(input, periodicity);
235   return new Profile(name,cb,cb.get_repeat_delay());
236 }
237
238 Profile* ProfileBuilder::from_file(const std::string& path)
239 {
240   xbt_assert(not path.empty(), "Cannot parse a trace from an empty filename");
241   auto f = std::unique_ptr<std::ifstream>(surf_ifsopen(path));
242   xbt_assert(not f->fail(), "Cannot open file '%s' (path=%s)", path.c_str(), (boost::join(surf_path, ":")).c_str());
243
244   std::stringstream buffer;
245   buffer << f->rdbuf();
246
247   LegacyUpdateCb cb(buffer.str(), -1);
248   return new Profile(path,cb,cb.get_repeat_delay());
249 }
250
251
252 Profile* ProfileBuilder::from_void() {
253   static Profile void_profile("__void__",[](std::vector<DatedValue>&){},-1.0);
254   return &void_profile;
255 }
256
257 Profile* ProfileBuilder::from_callback(const std::string& name, const std::function<UpdateCb>& cb, double repeat_delay) {
258   return new Profile(name, cb, repeat_delay);
259 }
260
261
262 } // namespace profile
263 } // namespace kernel
264 } // namespace simgrid