Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add new entry in Release_Notes.
[simgrid.git] / src / kernel / lmm / System.hpp
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 #ifndef SIMGRID_KERNEL_LMM_SYSTEM_HPP
7 #define SIMGRID_KERNEL_LMM_SYSTEM_HPP
8
9 #include "simgrid/kernel/resource/Action.hpp"
10 #include "simgrid/kernel/resource/Model.hpp"
11 #include "xbt/asserts.h"
12 #include "xbt/mallocator.h"
13
14 #include <boost/intrusive/list.hpp>
15 #include <cmath>
16 #include <limits>
17 #include <memory>
18 #include <string_view>
19 #include <vector>
20
21 /* user-visible parameters */
22 XBT_PUBLIC_DATA double sg_precision_workamount;
23 XBT_PUBLIC_DATA double sg_precision_timing;
24 XBT_PUBLIC_DATA int sg_concurrency_limit;
25
26 namespace simgrid::kernel::lmm {
27
28 /** @addtogroup Model_lmm
29  * @details
30  * A linear maxmin solver to resolve inequations systems.
31  *
32  * Most SimGrid model rely on a "fluid/steady-state" modeling that simulate the sharing of resources between actions at
33  * relatively coarse-grain.  Such sharing is generally done by solving a set of linear inequations. Let's take an
34  * example and assume we have the variables \f$x_1\f$, \f$x_2\f$, \f$x_3\f$, and \f$x_4\f$ . Let's say that \f$x_1\f$
35  * and \f$x_2\f$ correspond to activities running and the same CPU \f$A\f$ whose capacity is \f$C_A\f$. In such a
36  * case, we need to enforce:
37  *
38  *   \f[ x_1 + x_2 \leq C_A \f]
39  *
40  * Likewise, if \f$x_3\f$ (resp. \f$x_4\f$) corresponds to a network flow \f$F_3\f$ (resp. \f$F_4\f$) that goes through
41  * a set of links \f$L_1\f$ and \f$L_2\f$ (resp. \f$L_2\f$ and \f$L_3\f$), then we need to enforce:
42  *
43  *   \f[ x_3  \leq C_{L_1} \f]
44  *   \f[ x_3 + x_4 \leq C_{L_2} \f]
45  *   \f[ x_4 \leq C_{L_3} \f]
46  *
47  * One could set every variable to 0 to make sure the constraints are satisfied but this would obviously not be very
48  * realistic. A possible objective is to try to maximize the minimum of the \f$x_i\f$ . This ensures that all the
49  * \f$x_i\f$ are positive and "as large as possible".
50  *
51  * This is called *max-min fairness* and is the most commonly used objective in SimGrid. Another possibility is to
52  * maximize \f$\sum_if(x_i)\f$, where \f$f\f$ is a strictly increasing concave function.
53  *
54  * Constraint:
55  *  - bound (set)
56  *  - shared (set)
57  *  - usage (computed)
58  *
59  * Variable:
60  *  - weight (set)
61  *  - bound (set)
62  *  - value (computed)
63  *
64  * Element:
65  *  - value (set)
66  *
67  * A possible system could be:
68  * - three variables: `var1`, `var2`, `var3`
69  * - two constraints: `cons1`, `cons2`
70  * - four elements linking:
71  *  - `elem1` linking `var1` and `cons1`
72  *  - `elem2` linking `var2` and `cons1`
73  *  - `elem3` linking `var2` and `cons2`
74  *  - `elem4` linking `var3` and `cons2`
75  *
76  * And the corresponding inequations will be:
77  *
78  *     var1.value <= var1.bound
79  *     var2.value <= var2.bound
80  *     var3.value <= var3.bound
81  *     var1.weight * var1.value * elem1.value + var2.weight * var2.value * elem2.value <= cons1.bound
82  *     var2.weight * var2.value * elem3.value + var3.weight * var3.value * elem4.value <= cons2.bound
83  *
84  * where `var1.value`, `var2.value` and `var3.value` are the unknown values.
85  *
86  * If a constraint is not shared, the sum is replaced by a max.
87  * For example, a third non-shared constraint `cons3` and the associated elements `elem5` and `elem6` could write as:
88  *
89  *     max( var1.weight * var1.value * elem5.value  ,  var3.weight * var3.value * elem6.value ) <= cons3.bound
90  *
91  * This is useful for the sharing of resources for various models.
92  * For instance, for the network model, each link is associated to a constraint and each communication to a variable.
93  *
94  * Implementation details
95  *
96  * For implementation reasons, we are interested in distinguishing variables that actually participate to the
97  * computation of constraints, and those who are part of the equations but are stuck to zero.
98  * We call enabled variables, those which var.weight is strictly positive. Zero-weight variables are called disabled
99  * variables.
100  * Unfortunately this concept of enabled/disabled variables intersects with active/inactive variable.
101  * Semantically, the intent is similar, but the conditions under which a variable is active is slightly more strict
102  * than the conditions for it to be enabled.
103  * A variable is active only if its var.value is non-zero (and, by construction, its var.weight is non-zero).
104  * In general, variables remain disabled after their creation, which often models an initialization phase (e.g. first
105  * packet propagating in the network). Then, it is enabled by the corresponding model. Afterwards, the max-min solver
106  * (lmm_solve()) activates it when appropriate. It is possible that the variable is again disabled, e.g. to model the
107  * pausing of an action.
108  *
109  * Concurrency limit and maximum
110  *
111  * We call concurrency, the number of variables that can be enabled at any time for each constraint.
112  * From a model perspective, this "concurrency" often represents the number of actions that actually compete for one
113  * constraint.
114  * The LMM solver is able to limit the concurrency for each constraint, and to monitor its maximum value.
115  *
116  * One may want to limit the concurrency of constraints for essentially three reasons:
117  *  - Keep LMM system in a size that can be solved (it does not react very well with tens of thousands of variables per
118  *    constraint)
119  *  - Stay within parameters where the fluid model is accurate enough.
120  *  - Model serialization effects
121  *
122  * The concurrency limit can also be set to a negative value to disable concurrency limit. This can improve performance
123  * slightly.
124  *
125  * Overall, each constraint contains three fields related to concurrency:
126  *  - concurrency_limit which is the limit enforced by the solver
127  *  - concurrency_current which is the current concurrency
128  *  - concurrency_maximum which is the observed maximum concurrency
129  *
130  * Variables consumes the concurrency_limit of each constraint they are using.
131  * Each pair variable/constrainst is linked by a *single* Element object. Through this
132  * object and the respective methods (get_concurrency(), increase_concurrency() and decrease_concurrency()),
133  * the variable changes the constraint's concurrency.
134  * The amount of concurrency slack taken by each variable is determined by the Element::get_concurrency() method.
135  * At the current state, each variable counts as 1 if its consumption weight is greater than 1.
136  */
137
138 /** @{ @ingroup Model_lmm */
139
140 /**
141  * @brief LMM element
142  * Elements can be seen as glue between constraint objects and variable objects.
143  * Basically, each variable will have a set of elements, one for each constraint where it is involved.
144  * Then, it is used to list all variables involved in constraint through constraint's xxx_element_set lists, or
145  * vice-versa list all constraints for a given variable.
146  */
147 class XBT_PUBLIC Element {
148 public:
149   // Use rule-of-three, and implicitely disable the move constructor which should be 'noexcept' according to C++ Core
150   // Guidelines.
151   Element(Constraint* constraint, Variable* variable, double cweight);
152   Element(const Element&) = default;
153   ~Element()              = default;
154
155   /**
156    * @brief Gets the "weight" of this element for concurrency checks.
157    *
158    * This is the amount taken by this variable of the constraint's concurrency slack
159    *
160    * @return 1 if consumption_weight greater than 1, 0 otherwise
161    */
162   int get_concurrency() const;
163   /**
164    * @brief Decreases the constraint's concurrency
165    *
166    * Decreases the equivalent of get_concurrency() from the constraint related to this element
167    */
168   void decrease_concurrency();
169   /**
170    *  @brief Increase constraint concurrency
171    *  @param check_limit Don't check constraint concurrency overflow right now
172    */
173   void increase_concurrency(bool check_limit = true);
174
175   void make_active();
176   void make_inactive();
177
178   /* hookup to constraint */
179   boost::intrusive::list_member_hook<> enabled_element_set_hook;
180   boost::intrusive::list_member_hook<> disabled_element_set_hook;
181   boost::intrusive::list_member_hook<> active_element_set_hook;
182
183   Constraint* constraint;
184   Variable* variable;
185
186   // consumption_weight: impact of 1 byte or flop of your application onto the resource (in byte or flop)
187   //   - if CPU, then probably 1.
188   //   - If network, then 1 in forward direction and 0.05 backward for the ACKs
189   double consumption_weight;
190   // maximum consumption weight (can be different from consumption_weight with subflows/ptasks)
191   double max_consumption_weight;
192 };
193
194 class ConstraintLight {
195 public:
196   double remaining_over_usage;
197   Constraint* cnst;
198 };
199
200 /**
201  * @brief LMM constraint
202  * Each constraint contains several partially overlapping logical sets of elements:
203  * \li Disabled elements which variable's weight is zero. This variables are not at all processed by LMM, but eventually
204  *     the corresponding action will enable it (at least this is the idea).
205  * \li Enabled elements which variable's weight is non-zero. They are utilized in some LMM functions.
206  * \li Active elements which variable's weight is non-zero (i.e. it is enabled) AND its element value is non-zero.
207  *     LMM_solve iterates over active elements during resolution, dynamically making them active or inactive.
208  */
209 class XBT_PUBLIC Constraint {
210 public:
211   enum class SharingPolicy { WIFI = 3, NONLINEAR = 2, SHARED = 1, FATPIPE = 0 };
212
213   Constraint(resource::Resource* id_value, double bound_value);
214
215   /** @brief Unshare a constraint. */
216   void unshare() { sharing_policy_ = SharingPolicy::FATPIPE; }
217
218   /** @brief Set how a constraint is shared  */
219   void set_sharing_policy(SharingPolicy policy, const s4u::NonLinearResourceCb& cb);
220   /** @brief Check how a constraint is shared  */
221   SharingPolicy get_sharing_policy() const { return sharing_policy_; }
222
223   /** @brief Get the load of the constraint after the last lmm solve */
224   double get_load() const;
225
226   /** @brief Sets the concurrency limit for this constraint */
227   void set_concurrency_limit(int limit)
228   {
229     xbt_assert(limit < 0 || concurrency_maximum_ <= limit,
230                "New concurrency limit should be larger than observed concurrency maximum. Maybe you want to call"
231                " concurrency_maximum_reset() to reset the maximum?");
232     concurrency_limit_ = limit;
233   }
234
235   /** @brief Gets the concurrency limit for this constraint */
236   int get_concurrency_limit() const { return concurrency_limit_; }
237
238   /**
239    * @brief Reset the concurrency maximum for a given variable (we will update the maximum to reflect constraint
240    * evolution).
241    */
242   void reset_concurrency_maximum() { concurrency_maximum_ = 0; }
243
244   /** @brief Get the concurrency maximum for a given constraint (which reflects constraint evolution). */
245   int get_concurrency_maximum() const
246   {
247     xbt_assert(concurrency_limit_ < 0 || concurrency_maximum_ <= concurrency_limit_,
248                "Very bad: maximum observed concurrency is higher than limit. This is a bug, please report it.");
249     return concurrency_maximum_;
250   }
251
252   /**
253    * @brief Get constraint current concurrency slack
254    *
255    * This represents the "space" available for new variables in this contraint.
256    * A variable can be enabled and use this constraint if its get_concurrency() <= slack
257    *
258    * @return Constraint's slack
259    */
260   int get_concurrency_slack() const
261   {
262     return concurrency_limit_ < 0 ? std::numeric_limits<int>::max() : concurrency_limit_ - concurrency_current_;
263   }
264
265   /**
266    * @brief Get a var associated to a constraint
267    * @details Get the first variable of the next variable of elem if elem is not NULL
268    * @param elem A element of constraint of the constraint or NULL
269    * @return A variable associated to a constraint
270    */
271   Variable* get_variable(const Element** elem) const;
272
273   /**
274    * @brief Get a var associated to a constraint
275    * @details Get the first variable of the next variable of elem if elem is not NULL
276    * @param elem A element of constraint of the constraint or NULL
277    * @param nextelem A element of constraint of the constraint or NULL, the one after elem
278    * @param numelem parameter representing the number of elements to go
279    * @return A variable associated to a constraint
280    */
281   Variable* get_variable_safe(const Element** elem, const Element** nextelem, size_t* numelem) const;
282
283   /**
284    * @brief Get the data associated to a constraint
285    * @return The data associated to the constraint
286    */
287   resource::Resource* get_id() const { return id_; }
288
289   /* hookup to system */
290   boost::intrusive::list_member_hook<> constraint_set_hook_;
291   boost::intrusive::list_member_hook<> active_constraint_set_hook_;
292   boost::intrusive::list_member_hook<> modified_constraint_set_hook_;
293   boost::intrusive::list_member_hook<> saturated_constraint_set_hook_;
294   boost::intrusive::list<Element, boost::intrusive::member_hook<Element, boost::intrusive::list_member_hook<>,
295                                                                 &Element::enabled_element_set_hook>>
296       enabled_element_set_;
297   boost::intrusive::list<Element, boost::intrusive::member_hook<Element, boost::intrusive::list_member_hook<>,
298                                                                 &Element::disabled_element_set_hook>>
299       disabled_element_set_;
300   boost::intrusive::list<Element, boost::intrusive::member_hook<Element, boost::intrusive::list_member_hook<>,
301                                                                 &Element::active_element_set_hook>>
302       active_element_set_;
303   double remaining_ = 0.0;
304   double usage_     = 0.0;
305   double bound_;
306   double dynamic_bound_ = 0.0; //!< dynamic bound for this constraint, defined by user's callback
307   // TODO MARTIN Check maximum value across resources at the end of simulation and give a warning is more than e.g. 500
308   int concurrency_current_ = 0; /* The current concurrency */
309   int concurrency_maximum_ = 0; /* The maximum number of (enabled and disabled) variables associated to the constraint
310                                  * at any given time (essentially for tracing)*/
311
312   SharingPolicy sharing_policy_ = SharingPolicy::SHARED;
313   int rank_; // Only used in debug messages to identify the constraint
314   double lambda_               = 0.0;
315   double new_lambda_           = 0.0;
316   ConstraintLight* cnst_light_ = nullptr;
317   s4u::NonLinearResourceCb dyn_constraint_cb_;
318
319 private:
320   static int next_rank_;  // To give a separate rank_ to each constraint
321   int concurrency_limit_ = sg_concurrency_limit; /* The maximum number of variables that may be enabled at any time
322                                                   * (stage variables if necessary) */
323   resource::Resource* id_;
324 };
325
326 /**
327  * @brief LMM variable
328  *
329  * When something prevents us from enabling a variable, we "stage" the weight that we would have like to set, so that as
330  * soon as possible we enable the variable with desired weight
331  */
332 class XBT_PUBLIC Variable {
333 public:
334   void initialize(resource::Action* id_value, double sharing_penalty, double bound_value, size_t number_of_constraints,
335                   unsigned visited_value);
336
337   /** @brief Get the value of the variable after the last lmm solve */
338   double get_value() const { return value_; }
339
340   /** @brief Get the maximum value of the variable (-1.0 if no specified maximum) */
341   double get_bound() const { return bound_; }
342
343   /**
344    * @brief Get the numth constraint associated to the variable
345    * @param num The rank of constraint we want to get
346    * @return The numth constraint
347    */
348   Constraint* get_constraint(unsigned num) const { return num < cnsts_.size() ? cnsts_[num].constraint : nullptr; }
349
350   /**
351    * @brief Get the weight of the numth constraint associated to the variable
352    * @param num The rank of constraint we want to get
353    * @return The numth constraint
354    */
355   double get_constraint_weight(unsigned num) const
356   {
357     return num < cnsts_.size() ? cnsts_[num].consumption_weight : 0.0;
358   }
359
360   /** @brief Get the number of constraint associated to a variable */
361   size_t get_number_of_constraint() const { return cnsts_.size(); }
362
363   /** @brief Get the data associated to a variable */
364   resource::Action* get_id() const { return id_; }
365
366   /** @brief Get the penalty of a variable */
367   double get_penalty() const { return sharing_penalty_; }
368
369   /** @brief Measure the minimum concurrency slack across all constraints where the given var is involved */
370   int get_min_concurrency_slack() const;
371
372   /** @brief Check if a variable can be enabled
373    * Make sure to set staged_penalty before, if your intent is only to check concurrency
374    */
375   bool can_enable() const { return staged_sharing_penalty_ > 0 && get_min_concurrency_slack() > 0; }
376
377   /* hookup to system */
378   boost::intrusive::list_member_hook<> variable_set_hook_;
379   boost::intrusive::list_member_hook<> saturated_variable_set_hook_;
380
381   std::vector<Element> cnsts_;
382
383   // sharing_penalty: variable's impact on the resource during the sharing
384   //   if == 0, the variable is not considered by LMM
385   //   on CPU, actions with N threads have a sharing of N
386   //   on network, the actions with higher latency have a lesser sharing_penalty
387   double sharing_penalty_;
388
389   double staged_sharing_penalty_; /* If non-zero, variable is staged for addition as soon as maxconcurrency constraints
390                             will be met */
391   double bound_;
392   double value_;
393   resource::Action* id_;
394   int rank_;         // Only used in debug messages to identify the variable
395   unsigned visited_; /* used by System::update_modified_cnst_set() */
396   double mu_;
397
398 private:
399   static int next_rank_; // To give a separate rank_ to each variable
400 };
401
402 inline void Element::make_active()
403 {
404   constraint->active_element_set_.push_front(*this);
405 }
406 inline void Element::make_inactive()
407 {
408   if (active_element_set_hook.is_linked())
409     xbt::intrusive_erase(constraint->active_element_set_, *this);
410 }
411
412 /**
413  * @brief LMM system
414  */
415 class XBT_PUBLIC System {
416 public:
417   /**
418    * @brief Creates a new System solver
419    *
420    * @param solver_name Name of the solver to be used
421    * @param selective_update Enables lazy updates
422    * @return pointer to System instance
423    */
424   static System* build(std::string_view solver_name, bool selective_update);
425   /** @brief Validates solver configuration */
426   static void validate_solver(const std::string& solver_name);
427
428   /**
429    * @brief Create a new Linear MaxMim system
430    * @param selective_update whether we should do lazy updates
431    */
432   explicit System(bool selective_update);
433   /** @brief Free an existing Linear MaxMin system */
434   virtual ~System();
435
436   /**
437    * @brief Create a new Linear MaxMin constraint
438    * @param id Data associated to the constraint (e.g.: a network link)
439    * @param bound_value The bound value of the constraint
440    */
441   Constraint* constraint_new(resource::Resource* id, double bound_value);
442
443   /**
444    * @brief Create a new Linear MaxMin variable
445    * @param id Data associated to the variable (e.g.: a network communication)
446    * @param sharing_penalty The weight of the variable (0.0 if not used)
447    * @param bound The maximum value of the variable (-1.0 if no maximum value)
448    * @param number_of_constraints The maximum number of constraints to associate to the variable
449    */
450   Variable* variable_new(resource::Action* id, double sharing_penalty, double bound = -1.0,
451                          size_t number_of_constraints = 1);
452
453   /** @brief Get the list of modified actions since last solve() */
454   resource::Action::ModifiedSet* get_modified_action_set() const;
455
456   /**
457    * @brief Free a variable
458    * @param var The variable to free
459    */
460   void variable_free(Variable * var);
461
462   /** @brief Free all variables */
463   void variable_free_all();
464
465   /**
466    * @brief Associate a variable to a constraint with a coefficient
467    * @param cnst A constraint
468    * @param var A variable
469    * @param value The coefficient associated to the variable in the constraint
470    * @param force_creation Force the creation of new element linking the variable to the constraint. Should be used only
471    * by the model ptask_L07 to cope with ptasks composed of flows running on the same resource (see
472    * https://framagit.org/simgrid/simgrid/-/issues/111)
473    */
474   void expand(Constraint* cnst, Variable* var, double value, bool force_creation = false);
475
476   /** @brief Update the bound of a variable */
477   void update_variable_bound(Variable * var, double bound);
478
479   /** @brief Update the sharing penalty of a variable */
480   void update_variable_penalty(Variable* var, double penalty);
481
482   /** @brief Update a constraint bound */
483   void update_constraint_bound(Constraint * cnst, double bound);
484
485   int constraint_used(const Constraint* cnst) const { return cnst->active_constraint_set_hook_.is_linked(); }
486
487   /** @brief Print the lmm system */
488   void print() const;
489
490   /** @brief Solve the lmm system. May be specialized in subclasses. */
491   void solve();
492
493 private:
494   static void* variable_mallocator_new_f();
495   static void variable_mallocator_free_f(void* var);
496   /** @brief Implements the solver. Must be specialized in subclasses. */
497   virtual void do_solve() = 0;
498
499   void var_free(Variable * var);
500   void cnst_free(Constraint * cnst);
501   Variable* extract_variable()
502   {
503     if (variable_set.empty())
504       return nullptr;
505     Variable* res = &variable_set.front();
506     variable_set.pop_front();
507     return res;
508   }
509   Constraint* extract_constraint()
510   {
511     if (constraint_set.empty())
512       return nullptr;
513     Constraint* res = &constraint_set.front();
514     constraint_set.pop_front();
515     return res;
516   }
517   void insert_constraint(Constraint * cnst) { constraint_set.push_back(*cnst); }
518   void remove_variable(Variable * var)
519   {
520     if (var->variable_set_hook_.is_linked())
521       xbt::intrusive_erase(variable_set, *var);
522     if (var->saturated_variable_set_hook_.is_linked())
523       xbt::intrusive_erase(saturated_variable_set, *var);
524   }
525   void make_constraint_active(Constraint * cnst)
526   {
527     if (not cnst->active_constraint_set_hook_.is_linked())
528       active_constraint_set.push_back(*cnst);
529   }
530   void make_constraint_inactive(Constraint * cnst)
531   {
532     if (cnst->active_constraint_set_hook_.is_linked())
533       xbt::intrusive_erase(active_constraint_set, *cnst);
534     if (cnst->modified_constraint_set_hook_.is_linked())
535       xbt::intrusive_erase(modified_constraint_set, *cnst);
536   }
537
538   void enable_var(Variable * var);
539   void disable_var(Variable * var);
540   void on_disabled_var(Constraint * cnstr);
541   void check_concurrency() const;
542
543   /**
544    * @brief Auxiliary method to create a new Element which links a variable to a constraint
545    *
546    * @param cnst Constraint (resource)
547    * @param var Variable (action)
548    * @param consumption_weight how much of the resource is used for each unit of the action
549    * @return A reference to the new element
550    */
551   Element& expand_create_elem(Constraint* cnst, Variable* var, double consumption_weight);
552   /**
553    * @brief Increments the element usage
554    *
555    * @param elem Element linking variable/action to resource
556    * @param cnst Constraint (resource)
557    * @param consumption_weight how much of the resource is used for each unit of the action
558    * @return elem itself
559    */
560   Element& expand_add_to_elem(Element& elem, const Constraint* cnst, double consumption_weight) const;
561
562   /**
563    * @brief Update the value of element linking the constraint and the variable
564    * @param cnst A constraint
565    * @param var A variable
566    * @param value The new value
567    */
568   void update(Constraint * cnst, Variable * var, double value);
569
570   /** @brief Given a variable, update modified_constraint_set_ */
571   void update_modified_cnst_set_from_variable(const Variable* var);
572   void update_modified_cnst_set(Constraint* cnst);
573   void update_modified_cnst_set_rec(const Constraint* cnst);
574   /** @brief Remove all constraints of the modified_constraint_set. */
575   void remove_all_modified_cnst_set();
576
577 public:
578   bool modified_ = false;
579   boost::intrusive::list<Variable, boost::intrusive::member_hook<Variable, boost::intrusive::list_member_hook<>,
580                                                                  &Variable::variable_set_hook_>>
581       variable_set;
582   boost::intrusive::list<Constraint, boost::intrusive::member_hook<Constraint, boost::intrusive::list_member_hook<>,
583                                                                    &Constraint::active_constraint_set_hook_>>
584       active_constraint_set;
585   boost::intrusive::list<Variable, boost::intrusive::member_hook<Variable, boost::intrusive::list_member_hook<>,
586                                                                  &Variable::saturated_variable_set_hook_>>
587       saturated_variable_set;
588   boost::intrusive::list<Constraint, boost::intrusive::member_hook<Constraint, boost::intrusive::list_member_hook<>,
589                                                                    &Constraint::saturated_constraint_set_hook_>>
590       saturated_constraint_set;
591
592 protected:
593   const bool selective_update_active; /* flag to update partially the system only selecting changed portions */
594   boost::intrusive::list<Constraint, boost::intrusive::member_hook<Constraint, boost::intrusive::list_member_hook<>,
595                                                                    &Constraint::modified_constraint_set_hook_>>
596       modified_constraint_set;
597
598 private:
599   unsigned visited_counter_ =
600       1; /* used by System::update_modified_cnst_set() and System::remove_all_modified_cnst_set() to
601           * cleverly (un-)flag the constraints (more details in these functions) */
602   boost::intrusive::list<Constraint, boost::intrusive::member_hook<Constraint, boost::intrusive::list_member_hook<>,
603                                                                    &Constraint::constraint_set_hook_>>
604       constraint_set;
605   xbt_mallocator_t variable_mallocator_ =
606       xbt_mallocator_new(65536, System::variable_mallocator_new_f, System::variable_mallocator_free_f, nullptr);
607
608   std::unique_ptr<resource::Action::ModifiedSet> modified_set_ = nullptr;
609 };
610
611 /** @} */
612 } // namespace simgrid::kernel::lmm
613
614 #endif