From 36fccba5f4f2b4c32e97c4e56911eb5bd804f7c9 Mon Sep 17 00:00:00 2001 From: Bruno Donassolo Date: Thu, 10 Jun 2021 18:31:38 +0200 Subject: [PATCH] Disk: Non-linear contraints 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. --- MANIFEST.in | 2 + examples/cpp/CMakeLists.txt | 2 +- .../cpp/io-degradation/s4u-io-degradation.cpp | 157 ++++++++++++++++++ .../io-degradation/s4u-io-degradation.tesh | 32 ++++ include/simgrid/forward.h | 7 + include/simgrid/s4u/Disk.hpp | 20 +++ src/kernel/lmm/maxmin.cpp | 27 ++- src/kernel/lmm/maxmin.hpp | 8 +- src/kernel/lmm/maxmin_test.cpp | 144 ++++++++++++++++ src/kernel/resource/DiskImpl.cpp | 38 +++++ src/kernel/resource/DiskImpl.hpp | 10 ++ src/s4u/s4u_Disk.cpp | 11 ++ src/surf/LinkImpl.cpp | 2 +- 13 files changed, 449 insertions(+), 11 deletions(-) create mode 100644 examples/cpp/io-degradation/s4u-io-degradation.cpp create mode 100644 examples/cpp/io-degradation/s4u-io-degradation.tesh diff --git a/MANIFEST.in b/MANIFEST.in index e794796615..d35fde725a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -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 diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 0d44e129ba..dfb36281f7 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -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 index 0000000000..30ae4aa990 --- /dev/null +++ b/examples/cpp/io-degradation/s4u-io-degradation.cpp @@ -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 + +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 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; + static const std::unordered_map 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 index 0000000000..4ede3fb35b --- /dev/null +++ b/examples/cpp/io-degradation/s4u-io-degradation.tesh @@ -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 diff --git a/include/simgrid/forward.h b/include/simgrid/forward.h index 0d46aa683a..664114e2ec 100644 --- a/include/simgrid/forward.h +++ b/include/simgrid/forward.h @@ -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; } // namespace s4u namespace config { diff --git a/include/simgrid/s4u/Disk.hpp b/include/simgrid/s4u/Disk.hpp index 8fbc6b54a7..fcd8a651ab 100644 --- a/include/simgrid/s4u/Disk.hpp +++ b/include/simgrid/s4u/Disk.hpp @@ -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 */ diff --git a/src/kernel/lmm/maxmin.cpp b/src/kernel/lmm/maxmin.cpp index 725149291d..4f347175d6 100644 --- a/src/kernel/lmm/maxmin.cpp +++ b/src/kernel/lmm/maxmin.cpp @@ -490,8 +490,12 @@ template 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 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 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 diff --git a/src/kernel/lmm/maxmin.hpp b/src/kernel/lmm/maxmin.hpp index d85e609a73..1dd0c539b2 100644 --- a/src/kernel/lmm/maxmin.hpp +++ b/src/kernel/lmm/maxmin.hpp @@ -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 diff --git a/src/kernel/lmm/maxmin_test.cpp b/src/kernel/lmm/maxmin_test.cpp index bb802fe248..5d754ade85 100644 --- a/src/kernel/lmm/maxmin_test.cpp +++ b/src/kernel/lmm/maxmin_test.cpp @@ -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 diff --git a/src/kernel/resource/DiskImpl.cpp b/src/kernel/resource/DiskImpl.cpp index fda3775762..a027ee739b 100644 --- a/src/kernel/resource/DiskImpl.cpp +++ b/src/kernel/resource/DiskImpl.cpp @@ -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 * **********/ diff --git a/src/kernel/resource/DiskImpl.hpp b/src/kernel/resource/DiskImpl.hpp index 694972bc7d..c7835b6fc9 100644 --- a/src/kernel/resource/DiskImpl.hpp +++ b/src/kernel/resource/DiskImpl.hpp @@ -51,6 +51,13 @@ class DiskImpl : public Resource_T, 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 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 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; diff --git a/src/s4u/s4u_Disk.cpp b/src/s4u/s4u_Disk.cpp index 735eeaeda0..29adfd31d4 100644 --- a/src/s4u/s4u_Disk.cpp +++ b/src/s4u/s4u_Disk.cpp @@ -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(); }); diff --git a/src/surf/LinkImpl.cpp b/src/surf/LinkImpl.cpp index 3bef2d1730..6b66ce4818 100644 --- a/src/surf/LinkImpl.cpp +++ b/src/surf/LinkImpl.cpp @@ -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 -- 2.20.1