Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'add_barrier_python_bindings' 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
49  * two successive iterations. Hence, PERIODICITY only has meaning for completely deterministic profiles. LOOP_AFTER
50  * represents an additional delay between the last pair of a profile pattern iteration and the first pair of the next
51  * iteration.
52  *
53  * Local instructions define one {date,value} pair, or more exactly one pattern.
54  * In effect, when a profile loops or has stochastic components, the actual {date,value} pairs will be generated
55  * dynamically. Roughly, a local instruction is a line with the following syntax: <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
66  * the first iteration.
67  */
68
69 class LegacyUpdateCb {
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) { return (val == "EXP" || val == "EXPONENTIAL"); }
89
90   static bool is_uniform_distribution(const std::string& val) { return (val == "UNIF" || val == "UNIFORM"); }
91
92 public:
93   LegacyUpdateCb(const std::string& input, double periodicity) : stochastic(false), loop(periodicity > 0), loop_delay(0)
94   {
95     int linecount = 0;
96     std::vector<std::string> list;
97     double last_date = 0;
98     boost::split(list, input, boost::is_any_of("\n\r"));
99     for (auto val : list) {
100       simgrid::kernel::profile::StochasticDatedValue stochevent;
101       linecount++;
102       boost::trim(val);
103       if (is_comment_or_empty_line(val))
104         continue;
105       if (sscanf(val.c_str(), "PERIODICITY %lg\n", &periodicity) == 1) {
106         loop = true;
107         continue;
108       }
109       if (sscanf(val.c_str(), "LOOPAFTER %lg\n", &loop_delay) == 1) {
110         loop = true;
111         continue;
112       }
113       if (val == "STOCHASTIC LOOP") {
114         stochastic = true;
115         loop       = true;
116         continue;
117       }
118       if (val == "STOCHASTIC") {
119         stochastic = true;
120         continue;
121       }
122
123       unsigned int i;
124       unsigned int j;
125       std::istringstream iss(val);
126       std::vector<std::string> splittedval((std::istream_iterator<std::string>(iss)),
127                                            std::istream_iterator<std::string>());
128
129       xbt_assert(not splittedval.empty(), "Invalid profile line");
130
131       if (splittedval[0] == "DET") {
132         stochevent.date_law = Distribution::DET;
133         i                   = 2;
134       } else if (is_normal_distribution(splittedval[0])) {
135         stochevent.date_law = Distribution::NORM;
136         i                   = 3;
137       } else if (is_exponential_distribution(splittedval[0])) {
138         stochevent.date_law = Distribution::EXP;
139         i                   = 2;
140       } else if (is_uniform_distribution(splittedval[0])) {
141         stochevent.date_law = Distribution::UNIF;
142         i                   = 3;
143       } else {
144         xbt_assert(not stochastic);
145         stochevent.date_law = Distribution::DET;
146         i                   = 1;
147       }
148
149       xbt_assert(splittedval.size() > i, "Invalid profile line");
150       if (i == 1 or i == 2) {
151         stochevent.date_params = {std::stod(splittedval[i - 1])};
152       } else if (i == 3) {
153         stochevent.date_params = {std::stod(splittedval[1]), std::stod(splittedval[2])};
154       }
155
156       if (splittedval[i] == "DET") {
157         stochevent.value_law = Distribution::DET;
158         j                    = 1;
159       } else if (is_normal_distribution(splittedval[i])) {
160         stochevent.value_law = Distribution::NORM;
161         j                    = 2;
162       } else if (is_exponential_distribution(splittedval[i])) {
163         stochevent.value_law = Distribution::EXP;
164         j                    = 1;
165       } else if (is_uniform_distribution(splittedval[i])) {
166         stochevent.value_law = Distribution::UNIF;
167         j                    = 2;
168       } else {
169         xbt_assert(not stochastic);
170         stochevent.value_law = Distribution::DET;
171         j                    = 0;
172       }
173
174       xbt_assert(splittedval.size() > i + j, "Invalid profile line");
175       if (j == 0 or j == 1) {
176         stochevent.value_params = {std::stod(splittedval[i + j])};
177       } else if (j == 2) {
178         stochevent.value_params = {std::stod(splittedval[i + 1]), std::stod(splittedval[i + 2])};
179       }
180
181       if (not stochastic) {
182         // In this mode, dates read from the string are absolute values
183         double new_date = stochevent.date_params[0];
184         xbt_assert(new_date >= 0, "Profile time value is negative, why?");
185         xbt_assert(last_date <= new_date, "%d: Invalid trace: Events must be sorted, but time %g > time %g.\n%s",
186                    linecount, last_date, new_date, input.c_str());
187         stochevent.date_params[0] -= last_date;
188         last_date = new_date;
189       }
190
191       pattern.emplace_back(stochevent);
192     }
193
194     xbt_assert(not stochastic or periodicity <= 0, "If you want periodicity with stochastic profiles, use LOOP_AFTER");
195     if (periodicity > 0) {
196       xbt_assert(loop and loop_delay == 0);
197       loop_delay = periodicity - last_date;
198     }
199
200     xbt_assert(loop_delay >= 0, "Profile loop conditions are not realizable!");
201   }
202
203   double get_repeat_delay()
204   {
205     if (not stochastic and loop)
206       return loop_delay;
207     return -1.0;
208   }
209
210   void operator()(std::vector<DatedValue>& event_list)
211   {
212     size_t initial_size = event_list.size();
213     if (loop or not initial_size) {
214       for (auto dv : pattern)
215         event_list.emplace_back(dv.get_datedvalue());
216       if (initial_size)
217         event_list.at(initial_size).date_ += loop_delay;
218     }
219   }
220 };
221
222 Profile* ProfileBuilder::from_string(const std::string& name, const std::string& input, double periodicity)
223 {
224   LegacyUpdateCb cb(input, periodicity);
225   return new Profile(name,cb,cb.get_repeat_delay());
226 }
227
228 Profile* ProfileBuilder::from_file(const std::string& path)
229 {
230   xbt_assert(not path.empty(), "Cannot parse a trace from an empty filename");
231   auto f = std::unique_ptr<std::ifstream>(surf_ifsopen(path));
232   xbt_assert(not f->fail(), "Cannot open file '%s' (path=%s)", path.c_str(), (boost::join(surf_path, ":")).c_str());
233
234   std::stringstream buffer;
235   buffer << f->rdbuf();
236
237   LegacyUpdateCb cb(buffer.str(), -1);
238   return new Profile(path,cb,cb.get_repeat_delay());
239 }
240
241
242 Profile* ProfileBuilder::from_void() {
243   static Profile void_profile("__void__",[](std::vector<DatedValue>&){},-1.0);
244   return &void_profile;
245 }
246
247 Profile* ProfileBuilder::from_callback(const std::string& name, const std::function<UpdateCb>& cb, double repeat_delay) {
248   return new Profile(name, cb, repeat_delay);
249 }
250
251
252 } // namespace profile
253 } // namespace kernel
254 } // namespace simgrid