Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Disk: Non-linear contraints
authorBruno Donassolo <bruno.donassolo@inria.fr>
Thu, 10 Jun 2021 16:31:38 +0000 (18:31 +0200)
committerBruno Donassolo <bruno.donassolo@inria.fr>
Thu, 15 Jul 2021 09:31:49 +0000 (11:31 +0200)
Users can now define a callback to set the capacity of the resource
during the system solve. The callback receives the current capacity and
number of activities/flows currently sharing the resource.

The capacity is defined once at the beginning of the system solve.

- Disk:
  - Add API to set sharing_policy by type: read/write/read-write
  - Example: io-degradation: simulates the degradation in read/write
operation on disks.

13 files changed:
MANIFEST.in
examples/cpp/CMakeLists.txt
examples/cpp/io-degradation/s4u-io-degradation.cpp [new file with mode: 0644]
examples/cpp/io-degradation/s4u-io-degradation.tesh [new file with mode: 0644]
include/simgrid/forward.h
include/simgrid/s4u/Disk.hpp
src/kernel/lmm/maxmin.cpp
src/kernel/lmm/maxmin.hpp
src/kernel/lmm/maxmin_test.cpp
src/kernel/resource/DiskImpl.cpp
src/kernel/resource/DiskImpl.hpp
src/s4u/s4u_Disk.cpp
src/surf/LinkImpl.cpp

index e794796..d35fde7 100644 (file)
@@ -252,6 +252,8 @@ include examples/cpp/exec-waitfor/s4u-exec-waitfor.cpp
 include examples/cpp/exec-waitfor/s4u-exec-waitfor.tesh
 include examples/cpp/io-async/s4u-io-async.cpp
 include examples/cpp/io-async/s4u-io-async.tesh
+include examples/cpp/io-degradation/s4u-io-degradation.cpp
+include examples/cpp/io-degradation/s4u-io-degradation.tesh
 include examples/cpp/io-dependent/s4u-io-dependent.cpp
 include examples/cpp/io-dependent/s4u-io-dependent.tesh
 include examples/cpp/io-disk-raw/s4u-io-disk-raw.cpp
index 0d44e12..dfb3628 100644 (file)
@@ -74,7 +74,7 @@ foreach (example actor-create actor-daemon actor-exiting actor-join actor-kill
                  maestro-set
                  mc-bugged1 mc-bugged2 mc-electric-fence mc-failing-assert
                                 network-wifi
-                 io-async io-file-system io-file-remote io-disk-raw io-dependent
+                 io-async io-degradation io-file-system io-file-remote io-disk-raw io-dependent
                  platform-failures platform-profile platform-properties
                  plugin-host-load plugin-link-load plugin-prodcons
                  replay-comm replay-io
diff --git a/examples/cpp/io-degradation/s4u-io-degradation.cpp b/examples/cpp/io-degradation/s4u-io-degradation.cpp
new file mode 100644 (file)
index 0000000..30ae4aa
--- /dev/null
@@ -0,0 +1,157 @@
+/* Copyright (c) 2017-2021. The SimGrid Team. All rights reserved.          */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+/* This example shows how to simulate a non-linear resource sharing for disk
+ * operations.
+ *
+ * It is inspired on the paper
+ * "Adding Storage Simulation Capacities to the SimGridToolkit: Concepts, Models, and API"
+ * Available at : https://hal.inria.fr/hal-01197128/document
+ *
+ * It shows how to simulate concurrent operations degrading overall performance of IO
+ * operations (specifically the effects presented in Fig. 8 of the paper).
+ */
+
+#include <simgrid/s4u.hpp>
+
+namespace sg4 = simgrid::s4u;
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(disk_test, "Messages specific for this simulation");
+
+/** @brief Calculates the bandwidth for disk doing async operations */
+static void estimate_bw(sg4::Disk* disk, int n_flows, bool read)
+{
+  unsigned long long size = 100000;
+  double cur_time         = sg4::Engine::get_clock();
+  std::vector<sg4::IoPtr> activities;
+  for (int i = 0; i < n_flows; i++) {
+    sg4::IoPtr act;
+    if (read)
+      act = disk->read_async(size);
+    else
+      act = disk->write_async(size);
+
+    activities.push_back(act);
+  }
+
+  for (auto& act : activities)
+    act->wait();
+
+  double elapsed_time = sg4::Engine::get_clock() - cur_time;
+  double estimated_bw = size * n_flows / elapsed_time;
+  XBT_INFO("Disk: %s, concurrent %s: %d, estimated bandwidth: %lf", disk->get_cname(), read ? "read" : "write", n_flows,
+           estimated_bw);
+}
+
+static void host()
+{
+  /* - Estimating bw for each disk and considering concurrent flows */
+  for (int n = 1; n < 15; n += 2) {
+    for (auto* disk : sg4::Host::current()->get_disks()) {
+      estimate_bw(disk, n, true);
+      estimate_bw(disk, n, false);
+    }
+  }
+}
+
+/**
+ * @brief Non-linear resource callback for SSD disks
+ *
+ * In this case, we have measurements for some resource sharing and directly use them to return the
+ * correct value
+ * @param disk Disk on which the operation is happening (defined by the user through the std::bind)
+ * @param op read or write operation (defined by the user through the std::bind)
+ * @param capacity Resource current capacity in SimGrid
+ * @param n Number of activities sharing this resource
+ */
+static double ssd_dynamic_sharing(const sg4::Disk* disk, const std::string& op, double capacity, int n)
+{
+  /* measurements for SSD disks */
+  using DiskCapacity                                                   = std::unordered_map<int, double>;
+  static const std::unordered_map<std::string, DiskCapacity> SSD_SPEED = {{"write", {{1, 131.}}},
+                                                                          {"read",
+                                                                           {{1, 152.},
+                                                                            {2, 161.},
+                                                                            {3, 184.},
+                                                                            {4, 197.},
+                                                                            {5, 207.},
+                                                                            {6, 215.},
+                                                                            {7, 220.},
+                                                                            {8, 224.},
+                                                                            {9, 227.},
+                                                                            {10, 231.},
+                                                                            {11, 233.},
+                                                                            {12, 235.},
+                                                                            {13, 237.},
+                                                                            {14, 238.},
+                                                                            {15, 239.}}}};
+
+  const auto& data = SSD_SPEED.at(op);
+  /* no special bandwidth for this disk sharing N flows, just returns maximal capacity */
+  if (data.find(n) != data.end())
+    capacity = data.at(n);
+
+  // XBT_INFO("Disk %s, %s operation between %d flows, capacity %lf", disk->get_cname(), op.c_str(), n, capacity);
+
+  return capacity;
+}
+
+/**
+ * @brief Non-linear resource callback for SATA disks
+ *
+ * In this case, the degradation for read operations is linear and we have a formula that represents it.
+ *
+ * @param disk Disk on which the operation is happening (defined by the user through the std::bind)
+ * @param capacity Resource current capacity in SimGrid
+ * @param n Number of activities sharing this resource
+ */
+static double sata_dynamic_sharing(const sg4::Disk* disk, double capacity, int n)
+{
+  capacity = 68.3 - 1.7 * n;
+  // XBT_INFO("Disk %s, read operation between %d flows, capacity %lf", disk->get_cname(), n, capacity);
+
+  return capacity;
+}
+
+/** @brief Creates an SSD disk, setting the appropriate callback for non-linear resource sharing */
+static void create_ssd_disk(sg4::Host* host, const std::string& disk_name)
+{
+  auto* disk = host->create_disk(disk_name, "240MBps", "170MBps");
+  disk->set_sharing_policy(sg4::Disk::Operation::READ, sg4::Disk::SharingPolicy::NONLINEAR,
+                           std::bind(&ssd_dynamic_sharing, disk, "read", std::placeholders::_1, std::placeholders::_2));
+  disk->set_sharing_policy(
+      sg4::Disk::Operation::WRITE, sg4::Disk::SharingPolicy::NONLINEAR,
+      std::bind(&ssd_dynamic_sharing, disk, "write", std::placeholders::_1, std::placeholders::_2));
+  disk->set_sharing_policy(sg4::Disk::Operation::READWRITE, sg4::Disk::SharingPolicy::LINEAR);
+}
+
+/** @brief Same for a SATA disk, only read operation follows a non-linear resource sharing */
+static void create_sata_disk(sg4::Host* host, const std::string& disk_name)
+{
+  auto* disk = host->create_disk(disk_name, "68MBps", "50MBps");
+  disk->set_sharing_policy(sg4::Disk::Operation::READ, sg4::Disk::SharingPolicy::NONLINEAR,
+                           std::bind(&sata_dynamic_sharing, disk, std::placeholders::_1, std::placeholders::_2));
+  /* this is the default behavior, expliciting only to make it clearer */
+  disk->set_sharing_policy(sg4::Disk::Operation::WRITE, sg4::Disk::SharingPolicy::LINEAR);
+  disk->set_sharing_policy(sg4::Disk::Operation::READWRITE, sg4::Disk::SharingPolicy::LINEAR);
+}
+
+int main(int argc, char** argv)
+{
+  sg4::Engine e(&argc, argv);
+  /* simple platform containing 1 host and 2 disk */
+  auto* zone = sg4::create_full_zone("bob_zone");
+  auto* bob  = zone->create_host("bob", 1e6);
+  create_ssd_disk(bob, "Edel (SSD)");
+  create_sata_disk(bob, "Griffon (SATA II)");
+  zone->seal();
+
+  simgrid::s4u::Actor::create("", bob, host);
+
+  e.run();
+  XBT_INFO("Simulated time: %g", simgrid::s4u::Engine::get_clock());
+
+  return 0;
+}
diff --git a/examples/cpp/io-degradation/s4u-io-degradation.tesh b/examples/cpp/io-degradation/s4u-io-degradation.tesh
new file mode 100644 (file)
index 0000000..4ede3fb
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env tesh
+
+$ ${bindir}/s4u-io-degradation "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n"
+> [657.894737] (1:@bob) Disk: Edel (SSD), concurrent read: 1, estimated bandwidth: 152.000000
+> [1421.253515] (1:@bob) Disk: Edel (SSD), concurrent write: 1, estimated bandwidth: 131.000000
+> [2922.755017] (1:@bob) Disk: Griffon (SATA II), concurrent read: 1, estimated bandwidth: 66.600000
+> [2922.757017] (1:@bob) Disk: Griffon (SATA II), concurrent write: 1, estimated bandwidth: 50000000.001182
+> [4553.191800] (1:@bob) Disk: Edel (SSD), concurrent read: 3, estimated bandwidth: 184.000000
+> [4553.193564] (1:@bob) Disk: Edel (SSD), concurrent write: 3, estimated bandwidth: 170000000.022058
+> [9300.029007] (1:@bob) Disk: Griffon (SATA II), concurrent read: 3, estimated bandwidth: 63.200000
+> [9300.035007] (1:@bob) Disk: Griffon (SATA II), concurrent write: 3, estimated bandwidth: 50000000.004972
+> [11715.493945] (1:@bob) Disk: Edel (SSD), concurrent read: 5, estimated bandwidth: 207.000000
+> [11715.496886] (1:@bob) Disk: Edel (SSD), concurrent write: 5, estimated bandwidth: 170000000.039581
+> [20076.700899] (1:@bob) Disk: Griffon (SATA II), concurrent read: 5, estimated bandwidth: 59.800000
+> [20076.710899] (1:@bob) Disk: Griffon (SATA II), concurrent write: 5, estimated bandwidth: 50000000.008004
+> [23258.529081] (1:@bob) Disk: Edel (SSD), concurrent read: 7, estimated bandwidth: 220.000000
+> [23258.533199] (1:@bob) Disk: Edel (SSD), concurrent write: 7, estimated bandwidth: 170000000.009542
+> [35669.880716] (1:@bob) Disk: Griffon (SATA II), concurrent read: 7, estimated bandwidth: 56.400000
+> [35669.894716] (1:@bob) Disk: Griffon (SATA II), concurrent write: 7, estimated bandwidth: 49999999.989814
+> [39634.652426] (1:@bob) Disk: Edel (SSD), concurrent read: 9, estimated bandwidth: 227.000000
+> [39634.657720] (1:@bob) Disk: Edel (SSD), concurrent write: 9, estimated bandwidth: 169999999.992853
+> [56615.789795] (1:@bob) Disk: Griffon (SATA II), concurrent read: 9, estimated bandwidth: 53.000000
+> [56615.807795] (1:@bob) Disk: Griffon (SATA II), concurrent write: 9, estimated bandwidth: 50000000.010025
+> [61336.837838] (1:@bob) Disk: Edel (SSD), concurrent read: 11, estimated bandwidth: 233.000000
+> [61336.844309] (1:@bob) Disk: Edel (SSD), concurrent write: 11, estimated bandwidth: 170000000.077813
+> [83514.263663] (1:@bob) Disk: Griffon (SATA II), concurrent read: 11, estimated bandwidth: 49.600000
+> [83514.285663] (1:@bob) Disk: Griffon (SATA II), concurrent write: 11, estimated bandwidth: 50000000.006350
+> [88999.517731] (1:@bob) Disk: Edel (SSD), concurrent read: 13, estimated bandwidth: 237.000000
+> [88999.525378] (1:@bob) Disk: Edel (SSD), concurrent write: 13, estimated bandwidth: 169999999.974881
+> [117138.053517] (1:@bob) Disk: Griffon (SATA II), concurrent read: 13, estimated bandwidth: 46.200000
+> [117138.079517] (1:@bob) Disk: Griffon (SATA II), concurrent write: 13, estimated bandwidth: 50000000.003806
+> [117138.079517] (0:maestro@) Simulated time: 117138
index 0d46aa6..664114e 100644 (file)
@@ -91,6 +91,13 @@ XBT_PUBLIC void intrusive_ptr_release(const Semaphore* m);
 XBT_PUBLIC void intrusive_ptr_add_ref(const Semaphore* m);
 
 class Disk;
+/**
+ * @brief Callback to dynamically change the resource's capacity
+ *
+ * Allows user to change resource's capacity depending on the number of concurrent activities
+ * running on the resource at a given instant
+ */
+using NonLinearResourceCb = std::function<double(double capacity, int n_activities)>;
 } // namespace s4u
 
 namespace config {
index 8fbc6b5..fcd8a65 100644 (file)
@@ -77,6 +77,26 @@ public:
   IoPtr write_async(sg_size_t size) const;
   sg_size_t write(sg_size_t size) const;
 
+  /** @brief Policy for sharing the disk among activities */
+  enum class SharingPolicy { NONLINEAR = 1, LINEAR = 0 };
+  enum class Operation { READ = 2, WRITE = 1, READWRITE = 0 };
+
+  /**
+   * @brief Describes how the disk is shared between activities for each operation
+   *
+   * Disks have different bandwidths for read and write operations. This method
+   * allows you to set different sharing policies for each operation:
+   * - Read: resource sharing for read operation
+   * - Write: resource sharing for write
+   * - ReadWrite: global sharing for read and write operations
+   *
+   * @param op Operation type
+   * @param policy Sharing policy
+   * @param cb Callback for NONLINEAR policies
+   */
+  Disk* set_sharing_policy(Operation op, SharingPolicy policy, const s4u::NonLinearResourceCb& cb = {});
+  SharingPolicy get_sharing_policy(Operation op) const;
+
   Disk* seal();
 
   /* The signals */
index 7251492..4f34717 100644 (file)
@@ -490,8 +490,12 @@ template <class CnstList> void System::lmm_solve(CnstList& cnst_list)
   for (Constraint& cnst : cnst_list) {
     /* INIT: Collect constraints that actually need to be saturated (i.e remaining  and usage are strictly positive)
      * into cnst_light_tab. */
-    cnst.remaining_ = cnst.bound_;
-    if (not double_positive(cnst.remaining_, cnst.bound_ * sg_maxmin_precision))
+    cnst.dynamic_bound_ = cnst.bound_;
+    if (cnst.get_sharing_policy() == Constraint::SharingPolicy::NONLINEAR && cnst.dyn_constraint_cb_) {
+      cnst.dynamic_bound_ = cnst.dyn_constraint_cb_(cnst.bound_, cnst.concurrency_current_);
+    }
+    cnst.remaining_ = cnst.dynamic_bound_;
+    if (not double_positive(cnst.remaining_, cnst.dynamic_bound_ * sg_maxmin_precision))
       continue;
     cnst.usage_ = 0;
     for (Element& elem : cnst.enabled_element_set_) {
@@ -573,15 +577,16 @@ template <class CnstList> void System::lmm_solve(CnstList& cnst_list)
         Constraint* cnst = elem.constraint;
         if (cnst->sharing_policy_ != Constraint::SharingPolicy::FATPIPE) {
           // Remember: shared constraints require that sum(elem.value * var.value) < cnst->bound
-          double_update(&(cnst->remaining_), elem.consumption_weight * var.value_, cnst->bound_ * sg_maxmin_precision);
+          double_update(&(cnst->remaining_), elem.consumption_weight * var.value_,
+                        cnst->dynamic_bound_ * sg_maxmin_precision);
           double_update(&(cnst->usage_), elem.consumption_weight / var.sharing_penalty_, sg_maxmin_precision);
           // If the constraint is saturated, remove it from the set of active constraints (light_tab)
           if (not double_positive(cnst->usage_, sg_maxmin_precision) ||
-              not double_positive(cnst->remaining_, cnst->bound_ * sg_maxmin_precision)) {
+              not double_positive(cnst->remaining_, cnst->dynamic_bound_ * sg_maxmin_precision)) {
             if (cnst->cnst_light_) {
               size_t index = (cnst->cnst_light_ - cnst_light_tab);
               XBT_DEBUG("index: %zu \t cnst_light_num: %d \t || usage: %f remaining: %f bound: %f  ", index,
-                        cnst_light_num, cnst->usage_, cnst->remaining_, cnst->bound_);
+                        cnst_light_num, cnst->usage_, cnst->remaining_, cnst->dynamic_bound_);
               cnst_light_tab[index]                  = cnst_light_tab[cnst_light_num - 1];
               cnst_light_tab[index].cnst->cnst_light_ = &cnst_light_tab[index];
               cnst_light_num--;
@@ -606,13 +611,13 @@ template <class CnstList> void System::lmm_solve(CnstList& cnst_list)
           }
           // If the constraint is saturated, remove it from the set of active constraints (light_tab)
           if (not double_positive(cnst->usage_, sg_maxmin_precision) ||
-              not double_positive(cnst->remaining_, cnst->bound_ * sg_maxmin_precision)) {
+              not double_positive(cnst->remaining_, cnst->dynamic_bound_ * sg_maxmin_precision)) {
             if (cnst->cnst_light_) {
               size_t index = (cnst->cnst_light_ - cnst_light_tab);
               XBT_DEBUG("index: %zu \t cnst_light_num: %d \t || \t cnst: %p \t cnst->cnst_light: %p "
                         "\t cnst_light_tab: %p usage: %f remaining: %f bound: %f  ",
                         index, cnst_light_num, cnst, cnst->cnst_light_, cnst_light_tab, cnst->usage_, cnst->remaining_,
-                        cnst->bound_);
+                        cnst->dynamic_bound_);
               cnst_light_tab[index]                  = cnst_light_tab[cnst_light_num - 1];
               cnst_light_tab[index].cnst->cnst_light_ = &cnst_light_tab[index];
               cnst_light_num--;
@@ -932,6 +937,14 @@ int Constraint::get_variable_amount() const
                                         [](const Element& elem) { return elem.consumption_weight > 0; }));
 }
 
+void Constraint::set_sharing_policy(SharingPolicy policy, const s4u::NonLinearResourceCb& cb)
+{
+  xbt_assert(!cb || (cb && policy == SharingPolicy::NONLINEAR),
+             "Invalid sharing policy for constraint. Callback should be used with NONLINEAR sharing policy");
+  sharing_policy_    = policy;
+  dyn_constraint_cb_ = cb;
+}
+
 } // namespace lmm
 } // namespace kernel
 } // namespace simgrid
index d85e609..1dd0c53 100644 (file)
@@ -7,6 +7,7 @@
 #define SURF_MAXMIN_HPP
 
 #include "simgrid/kernel/resource/Action.hpp"
+#include "simgrid/kernel/resource/Model.hpp"
 #include "simgrid/s4u/Link.hpp"
 #include "src/surf/surf_interface.hpp"
 #include "xbt/asserts.h"
@@ -187,7 +188,8 @@ public:
  */
 class XBT_PUBLIC Constraint {
 public:
-  enum class SharingPolicy { SHARED = 1, FATPIPE = 0 };
+  enum class SharingPolicy { NONLINEAR = 2, SHARED = 1, FATPIPE = 0 };
+
   Constraint() = delete;
   Constraint(resource::Resource* id_value, double bound_value);
 
@@ -195,7 +197,7 @@ public:
   void unshare() { sharing_policy_ = SharingPolicy::FATPIPE; }
 
   /** @brief Set how a constraint is shared  */
-  void set_sharing_policy(SharingPolicy policy) { sharing_policy_ = policy; }
+  void set_sharing_policy(SharingPolicy policy, const s4u::NonLinearResourceCb& cb);
   /** @brief Check how a constraint is shared  */
   SharingPolicy get_sharing_policy() const { return sharing_policy_; }
 
@@ -275,6 +277,7 @@ public:
   double remaining_ = 0.0;
   double usage_     = 0.0;
   double bound_;
+  double dynamic_bound_; //!< dynamic bound for this constraint, defined by user's callback
   // TODO MARTIN Check maximum value across resources at the end of simulation and give a warning is more than e.g. 500
   int concurrency_current_ = 0; /* The current concurrency */
   int concurrency_maximum_ = 0; /* The maximum number of (enabled and disabled) variables associated to the constraint
@@ -285,6 +288,7 @@ public:
   double lambda_               = 0.0;
   double new_lambda_           = 0.0;
   ConstraintLight* cnst_light_ = nullptr;
+  s4u::NonLinearResourceCb dyn_constraint_cb_;
 
 private:
   static int next_rank_;  // To give a separate rank_ to each constraint
index bb802fe..5d754ad 100644 (file)
@@ -280,3 +280,147 @@ TEST_CASE("kernel::lmm Single constraint unshared systems", "[kernel-lmm-unshare
 
   Sys.variable_free_all();
 }
+
+TEST_CASE("kernel::lmm dynamic constraint shared systems", "[kernel-lmm-shared-single-sys]")
+{
+  auto cb = [](double bound, int flows) -> double {
+    // decrease 10 % for each extra flow sharing this resource
+    return bound - (flows - 1) * .10 * bound;
+  };
+  lmm::System Sys(false);
+  lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 10);
+  sys_cnst->set_sharing_policy(lmm::Constraint::SharingPolicy::NONLINEAR, cb);
+
+  SECTION("1 activity, 100% C")
+  {
+    /*
+     * A single variable gets all the share
+     *
+     * In details:
+     *   o System:  a1 * p1 * \rho1 < C
+     *   o consumption_weight: a1=1
+     *   o sharing_penalty:    p1=1
+     *
+     * Expectations
+     *   o rho1 = C (because all weights are 1)
+     */
+
+    lmm::Variable* rho_1      = Sys.variable_new(nullptr, 1);
+
+    Sys.expand(sys_cnst, rho_1, 1);
+    Sys.solve();
+
+    REQUIRE(double_equals(rho_1->get_value(), 10, sg_maxmin_precision));
+  }
+
+  SECTION("2 activities, but ignore crosstraffic 100% C")
+  {
+    /*
+     * Ignore small activities (e.g. crosstraffic)
+     *
+     * In details:
+     *   o System:  a1 * p1 * \rho1  +  a2 * p2 * \rho2 < C
+     *   o consumption_weight: a1=1 ; a2=0.05
+     *   o sharing_penalty:    p1=1 ; p2=1
+     *
+     * Expectations
+     *   o rho1 = C/1.05
+     *   o rho2 = C/1.05
+     *   o rho1 = rho2 (because rho1 and rho2 has the same penalty)
+     */
+
+    lmm::Variable* rho_1      = Sys.variable_new(nullptr, 1);
+    lmm::Variable* rho_2      = Sys.variable_new(nullptr, 1);
+
+    Sys.expand(sys_cnst, rho_1, 1);
+    Sys.expand(sys_cnst, rho_2, 0.05);
+    Sys.solve();
+
+    REQUIRE(double_equals(rho_1->get_value(), 10 / 1.05, sg_maxmin_precision));
+    REQUIRE(double_equals(rho_1->get_value(), rho_2->get_value(), sg_maxmin_precision));
+  }
+
+  SECTION("2 activities, 1 inactive 100% C")
+  {
+    /*
+     * 2 activities but 1 is inactive (sharing_penalty = 0)
+     *
+     * In details:
+     *   o System:  a1 * p1 * \rho1  +  a2 * p2 * \rho2 < C
+     *   o consumption_weight: a1=1 ; a2=1
+     *   o sharing_penalty:    p1=1 ; p2=0
+     *
+     * Expectations
+     *   o rho1 = C
+     *   o rho2 = 0
+     */
+
+    lmm::Variable* rho_1      = Sys.variable_new(nullptr, 1);
+    lmm::Variable* rho_2      = Sys.variable_new(nullptr, 0);
+
+    Sys.expand(sys_cnst, rho_1, 1);
+    Sys.expand(sys_cnst, rho_2, 1);
+    Sys.solve();
+
+    REQUIRE(double_equals(rho_1->get_value(), 10, sg_maxmin_precision));
+    REQUIRE(double_equals(rho_2->get_value(), 0, sg_maxmin_precision));
+  }
+
+  SECTION("2 activity, 90% C")
+  {
+    /*
+     * 2 similar variables degrades performance, but get same share
+     *
+     * In details:
+     *   o System:  a1 * p1 * \rho1  +  a2 * p2 * \rho2 < .9C
+     *   o consumption_weight: a1=1 ; a2=1
+     *   o sharing_penalty:    p1=1 ; p2=1
+     *
+     * Expectations
+     *   o rho1 = rho2
+     *   o rho1 + rho2 = C (because all weights are 1)
+     */
+
+    lmm::Variable* rho_1      = Sys.variable_new(nullptr, 1);
+    lmm::Variable* rho_2      = Sys.variable_new(nullptr, 1);
+
+    Sys.expand(sys_cnst, rho_1, 1);
+    Sys.expand(sys_cnst, rho_2, 1);
+    Sys.solve();
+
+    REQUIRE(double_equals(rho_1->get_value(), 4.5, sg_maxmin_precision));
+    REQUIRE(double_equals(rho_1->get_value(), 4.5, sg_maxmin_precision));
+  }
+
+  SECTION("3 activity, 80% C")
+  {
+    /*
+     * 3 similar variables degrades performance, sharing proportional to penalty
+     *
+     * In details:
+     *   o System:  a1 * p1 * \rho1  +  a2 * p2 * \rho2 + a3 * p3 * \rho3 < .8C
+     *   o consumption_weight: a1=1 ; a2=1 ; a3=1
+     *   o sharing_penalty:    p1=1 ; p2=3 ; p3=4
+     *
+     * Expectations
+     *   o rho1 = 2*rho2 = 2*rho3
+     *   0 rho1 = 4, rho2 = 2, rho3 = 2
+     *   o rho1 + rho2 + rho3 = .8C (because all weights are 1)
+     */
+
+    lmm::Variable* rho_1      = Sys.variable_new(nullptr, 1);
+    lmm::Variable* rho_2      = Sys.variable_new(nullptr, 2);
+    lmm::Variable* rho_3      = Sys.variable_new(nullptr, 2);
+
+    Sys.expand(sys_cnst, rho_1, 1);
+    Sys.expand(sys_cnst, rho_2, 1);
+    Sys.expand(sys_cnst, rho_3, 1);
+    Sys.solve();
+
+    REQUIRE(double_equals(rho_1->get_value(), 4, sg_maxmin_precision));
+    REQUIRE(double_equals(rho_2->get_value(), 2, sg_maxmin_precision));
+    REQUIRE(double_equals(rho_3->get_value(), 2, sg_maxmin_precision));
+  }
+
+  Sys.variable_free_all();
+}
\ No newline at end of file
index fda3775..a027ee7 100644 (file)
@@ -116,11 +116,49 @@ void DiskImpl::seal()
   this->set_read_constraint(maxmin_system->constraint_new(this, read_bw_.peak * read_bw_.scale))
       ->set_write_constraint(maxmin_system->constraint_new(this, write_bw_.peak * write_bw_.scale))
       ->set_constraint(maxmin_system->constraint_new(this, std::max(read_bw_.peak, write_bw_.peak)));
+  apply_sharing_policy_cfg();
   XBT_DEBUG("Create resource with read_bw '%f' write_bw '%f'", read_bw_.peak, write_bw_.peak);
   Resource::seal();
   turn_on();
 }
 
+constexpr kernel::lmm::Constraint::SharingPolicy to_maxmin_policy(s4u::Disk::SharingPolicy policy)
+{
+  switch (policy) {
+    case s4u::Disk::SharingPolicy::NONLINEAR:
+      return kernel::lmm::Constraint::SharingPolicy::NONLINEAR;
+    case s4u::Disk::SharingPolicy::LINEAR:
+    default:
+      return kernel::lmm::Constraint::SharingPolicy::SHARED;
+  }
+}
+
+void DiskImpl::set_sharing_policy(s4u::Disk::Operation op, s4u::Disk::SharingPolicy policy,
+                                  const s4u::NonLinearResourceCb& cb)
+{
+  sharing_policy_[op]    = policy;
+  sharing_policy_cb_[op] = cb;
+  apply_sharing_policy_cfg();
+}
+
+s4u::Disk::SharingPolicy DiskImpl::get_sharing_policy(s4u::Disk::Operation op) const
+{
+  return sharing_policy_.at(op);
+}
+
+void DiskImpl::apply_sharing_policy_cfg()
+{
+  if (get_constraint())
+    get_constraint()->set_sharing_policy(to_maxmin_policy(sharing_policy_[s4u::Disk::Operation::READWRITE]),
+                                         sharing_policy_cb_[s4u::Disk::Operation::READWRITE]);
+  if (constraint_read_)
+    constraint_read_->set_sharing_policy(to_maxmin_policy(sharing_policy_[s4u::Disk::Operation::READ]),
+                                         sharing_policy_cb_[s4u::Disk::Operation::READ]);
+  if (constraint_write_)
+    constraint_write_->set_sharing_policy(to_maxmin_policy(sharing_policy_[s4u::Disk::Operation::WRITE]),
+                                          sharing_policy_cb_[s4u::Disk::Operation::WRITE]);
+}
+
 /**********
  * Action *
  **********/
index 694972b..c7835b6 100644 (file)
@@ -51,6 +51,13 @@ class DiskImpl : public Resource_T<DiskImpl>, public xbt::PropertyHolder {
   s4u::Host* host_                   = nullptr;
   lmm::Constraint* constraint_write_ = nullptr; /* Constraint for maximum write bandwidth*/
   lmm::Constraint* constraint_read_  = nullptr; /* Constraint for maximum read bandwidth*/
+  std::unordered_map<s4u::Disk::Operation, s4u::Disk::SharingPolicy> sharing_policy_ = {
+      {s4u::Disk::Operation::READ, s4u::Disk::SharingPolicy::LINEAR},
+      {s4u::Disk::Operation::WRITE, s4u::Disk::SharingPolicy::LINEAR},
+      {s4u::Disk::Operation::READWRITE, s4u::Disk::SharingPolicy::LINEAR}};
+  std::unordered_map<s4u::Disk::Operation, s4u::NonLinearResourceCb> sharing_policy_cb_ = {};
+
+  void apply_sharing_policy_cfg();
 
 protected:
   ~DiskImpl() override = default; // Disallow direct deletion. Call destroy() instead.
@@ -84,6 +91,9 @@ public:
   DiskImpl* set_read_bandwidth_profile(profile::Profile* profile);
   DiskImpl* set_write_bandwidth_profile(profile::Profile* profile);
 
+  void set_sharing_policy(s4u::Disk::Operation op, s4u::Disk::SharingPolicy policy, const s4u::NonLinearResourceCb& cb);
+  s4u::Disk::SharingPolicy get_sharing_policy(s4u::Disk::Operation op) const;
+
   /** @brief Check if the Disk is used (if an action currently uses its resources) */
   bool is_used() const override;
   void turn_on() override;
index 735eeae..29adfd3 100644 (file)
@@ -131,6 +131,17 @@ sg_size_t Disk::write(sg_size_t size) const
   return IoPtr(io_init(size, Io::OpType::WRITE))->vetoable_start()->wait()->get_performed_ioops();
 }
 
+Disk* Disk::set_sharing_policy(Disk::Operation op, Disk::SharingPolicy policy, const NonLinearResourceCb& cb)
+{
+  kernel::actor::simcall([this, op, policy, &cb] { pimpl_->set_sharing_policy(op, policy, cb); });
+  return this;
+}
+
+Disk::SharingPolicy Disk::get_sharing_policy(Operation op) const
+{
+  return this->pimpl_->get_sharing_policy(op);
+}
+
 Disk* Disk::seal()
 {
   kernel::actor::simcall([this]{ pimpl_->seal(); });
index 3bef2d1..6b66ce4 100644 (file)
@@ -48,7 +48,7 @@ void LinkImpl::set_sharing_policy(s4u::Link::SharingPolicy policy)
   lmm::Constraint::SharingPolicy ct_policy = lmm::Constraint::SharingPolicy::SHARED;
   if (policy == s4u::Link::SharingPolicy::FATPIPE)
     ct_policy = lmm::Constraint::SharingPolicy::FATPIPE;
-  get_constraint()->set_sharing_policy(ct_policy);
+  get_constraint()->set_sharing_policy(ct_policy, {});
   sharing_policy_ = policy;
 }
 s4u::Link::SharingPolicy LinkImpl::get_sharing_policy() const