Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Update copyright lines for 2022.
[simgrid.git] / src / kernel / resource / profile / Profile.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 "src/kernel/resource/profile/Profile.hpp"
7 #include "simgrid/forward.h"
8 #include "src/kernel/resource/profile/DatedValue.hpp"
9 #include "src/kernel/resource/profile/Event.hpp"
10 #include "src/kernel/resource/profile/FutureEvtSet.hpp"
11 #include "src/kernel/resource/profile/StochasticDatedValue.hpp"
12 #include "src/surf/surf_interface.hpp"
13
14 #include <boost/algorithm/string.hpp>
15 #include <fstream>
16 #include <memory>
17 #include <ostream>
18 #include <sstream>
19 #include <unordered_map>
20 #include <vector>
21
22 static std::unordered_map<std::string, simgrid::kernel::profile::Profile*> trace_list;
23
24 namespace simgrid {
25 namespace kernel {
26 namespace profile {
27
28 Profile::Profile()
29 {
30   /* Add the first fake event storing the time at which the trace begins */
31   event_list.emplace_back(0, -1);
32   stochastic_event_list.emplace_back(0, -1);
33 }
34 Profile::~Profile() = default;
35
36 /** @brief Register this profile for that resource onto that FES,
37  * and get an iterator over the integrated trace  */
38 Event* Profile::schedule(FutureEvtSet* fes, resource::Resource* resource)
39 {
40   auto* event     = new Event();
41   event->profile  = this;
42   event->idx      = 0;
43   event->resource = resource;
44   event->free_me  = false;
45
46   xbt_assert((event->idx < event_list.size()), "Your profile should have at least one event!");
47
48   fes_ = fes;
49   fes_->add_event(0.0 /* start time */, event);
50   if (stochastic) {
51     xbt_assert(event->idx < stochastic_event_list.size(), "Your profile should have at least one stochastic event!");
52     futureDV = stochastic_event_list.at(event->idx).get_datedvalue();
53   }
54
55   return event;
56 }
57
58 /** @brief Gets the next event from a profile */
59 DatedValue Profile::next(Event* event)
60 {
61   double event_date  = fes_->next_date();
62
63   if (not stochastic) {
64     DatedValue dateVal = event_list.at(event->idx);
65
66     if (event->idx < event_list.size() - 1) {
67       fes_->add_event(event_date + dateVal.date_, event);
68       event->idx++;
69     } else if (dateVal.date_ > 0) { /* Last element. Shall we loop? */
70       fes_->add_event(event_date + dateVal.date_, event);
71       event->idx = 1; /* idx=0 is a placeholder to store when events really start */
72     } else {          /* If we don't loop, we don't need this event anymore */
73       event->free_me = true;
74     }
75     return dateVal;
76   } else {
77     DatedValue dateVal = futureDV;
78     if (event->idx < stochastic_event_list.size() - 1) {
79       event->idx++;
80     } else if (stochasticloop) { /* We have reached the last element and we have to loop. */
81       event->idx = 1;
82     } else {
83       event->free_me = true; /* We have reached the last element, but we don't need to loop. */
84     }
85
86     if (not event->free_me) { // In the case there is an element, we draw the next event
87       futureDV = stochastic_event_list.at(event->idx).get_datedvalue();
88       fes_->add_event(event_date + futureDV.date_, event);
89     }
90     return dateVal;
91   }
92 }
93
94 static bool is_comment_or_empty_line(const std::string& val)
95 {
96   return (val[0] == '#' || val[0] == '\0' || val[0] == '%');
97 }
98
99 static bool is_normal_distribution(const std::string& val)
100 {
101   return (val == "NORM" || val == "NORMAL" || val == "GAUSS" || val == "GAUSSIAN");
102 }
103
104 static bool is_exponential_distribution(const std::string& val)
105 {
106   return (val == "EXP" || val == "EXPONENTIAL");
107 }
108
109 static bool is_uniform_distribution(const std::string& val)
110 {
111   return (val == "UNIF" || val == "UNIFORM");
112 }
113
114 Profile* Profile::from_string(const std::string& name, const std::string& input, double periodicity)
115 {
116   int linecount                                    = 0;
117   auto* profile                                    = new simgrid::kernel::profile::Profile();
118   simgrid::kernel::profile::DatedValue* last_event = &(profile->event_list.back());
119
120   xbt_assert(trace_list.find(name) == trace_list.end(), "Refusing to define trace %s twice", name.c_str());
121
122   std::vector<std::string> list;
123   boost::split(list, input, boost::is_any_of("\n\r"));
124   for (auto val : list) {
125     simgrid::kernel::profile::DatedValue event;
126     simgrid::kernel::profile::StochasticDatedValue stochevent;
127     linecount++;
128     boost::trim(val);
129     if (is_comment_or_empty_line(val))
130       continue;
131     if (sscanf(val.c_str(), "PERIODICITY %lg\n", &periodicity) == 1)
132       continue;
133     if (sscanf(val.c_str(), "LOOPAFTER %lg\n", &periodicity) == 1)
134       continue;
135     if (val == "STOCHASTIC LOOP") {
136       profile->stochastic     = true;
137       profile->stochasticloop = true;
138       continue;
139     }
140     if (val == "STOCHASTIC") {
141       profile->stochastic = true;
142       continue;
143     }
144
145     if (profile->stochastic) {
146       unsigned int i;
147       unsigned int j;
148       std::istringstream iss(val);
149       std::vector<std::string> splittedval((std::istream_iterator<std::string>(iss)),
150                                            std::istream_iterator<std::string>());
151
152       xbt_assert(not splittedval.empty(), "Invalid profile line");
153
154       if (splittedval[0] == "DET") {
155         stochevent.date_law = Distribution::DET;
156         i                   = 2;
157       } else if (is_normal_distribution(splittedval[0])) {
158         stochevent.date_law = Distribution::NORM;
159         i                   = 3;
160       } else if (is_exponential_distribution(splittedval[0])) {
161         stochevent.date_law = Distribution::EXP;
162         i                   = 2;
163       } else if (is_uniform_distribution(splittedval[0])) {
164         stochevent.date_law = Distribution::UNIF;
165         i                   = 3;
166       } else {
167         xbt_die("Unknown law %s", splittedval[0].c_str());
168       }
169
170       xbt_assert(splittedval.size() > i, "Invalid profile line");
171       if (i == 2) {
172         stochevent.date_params = {std::stod(splittedval[1])};
173       } else if (i == 3) {
174         stochevent.date_params = {std::stod(splittedval[1]), std::stod(splittedval[2])};
175       }
176
177       if (splittedval[i] == "DET") {
178         stochevent.value_law = Distribution::DET;
179         j                    = 1;
180       } else if (is_normal_distribution(splittedval[i])) {
181         stochevent.value_law = Distribution::NORM;
182         j                    = 2;
183       } else if (is_exponential_distribution(splittedval[i])) {
184         stochevent.value_law = Distribution::EXP;
185         j                    = 1;
186       } else if (is_uniform_distribution(splittedval[i])) {
187         stochevent.value_law = Distribution::UNIF;
188         j                    = 2;
189       } else {
190         xbt_die("Unknown law %s", splittedval[i].c_str());
191       }
192
193       xbt_assert(splittedval.size() > i + j, "Invalid profile line");
194       if (j == 1) {
195         stochevent.value_params = {std::stod(splittedval[i + 1])};
196       } else if (j == 2) {
197         stochevent.value_params = {std::stod(splittedval[i + 1]), std::stod(splittedval[i + 2])};
198       }
199
200       profile->stochastic_event_list.emplace_back(stochevent);
201     } else {
202       xbt_assert(sscanf(val.c_str(), "%lg  %lg\n", &event.date_, &event.value_) == 2,
203                  "%s:%d: Syntax error in trace\n%s", name.c_str(), linecount, input.c_str());
204
205       xbt_assert(last_event->date_ <= event.date_,
206                  "%s:%d: Invalid trace: Events must be sorted, but time %g > time %g.\n%s", name.c_str(), linecount,
207                  last_event->date_, event.date_, input.c_str());
208       last_event->date_ = event.date_ - last_event->date_;
209
210       profile->event_list.emplace_back(event);
211       last_event = &(profile->event_list.back());
212     }
213   }
214   if (last_event) {
215     if (periodicity > 0) {
216       last_event->date_ = periodicity + profile->event_list.at(0).date_;
217     } else {
218       last_event->date_ = -1;
219     }
220   }
221
222   trace_list.insert({name, profile});
223
224   return profile;
225 }
226 Profile* Profile::from_file(const std::string& path)
227 {
228   xbt_assert(not path.empty(), "Cannot parse a trace from an empty filename");
229   xbt_assert(trace_list.find(path) == trace_list.end(), "Refusing to define trace %s twice", path.c_str());
230
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   return Profile::from_string(path, buffer.str(), -1);
238 }
239
240 } // namespace profile
241 } // namespace kernel
242 } // namespace simgrid
243
244 void tmgr_finalize()
245 {
246   for (auto const& kv : trace_list)
247     delete kv.second;
248   trace_list.clear();
249 }
250
251 void tmgr_trace_event_unref(simgrid::kernel::profile::Event** event)
252 {
253   if ((*event)->free_me) {
254     delete *event;
255     *event = nullptr;
256   }
257 }