Logo AND Algorithmique Numérique Distribuée

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