From: Bruno Donassolo Date: Fri, 18 Feb 2022 09:44:21 +0000 (+0100) Subject: New model for parallel tasks: host/model:ptask_BMF X-Git-Tag: v3.31~205^2~15 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/f0a819a9571424fb4b8bda0e8b05715813c18bc5 New model for parallel tasks: host/model:ptask_BMF Implement a new solver for lmm::System based on BMF (Bottleneck max fairness) objective. BMF provides a more realistic sharing of heterogeneous resources as used by parallel tasks. Enable it using: --cfg=host/model:ptask_BMF instead of ptask_L07. SimGrid compilation from source now requires a new library: Eigen3. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index d1b67ba5ec..920f7f8f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,9 @@ endif() set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads) +### Check for Eigen library +find_package (Eigen3 3.3 REQUIRED NO_MODULE) + ### Setup Options include(${CMAKE_HOME_DIRECTORY}/tools/cmake/Option.cmake) diff --git a/src/kernel/lmm/bmf.cpp b/src/kernel/lmm/bmf.cpp new file mode 100644 index 0000000000..91ca26e2d7 --- /dev/null +++ b/src/kernel/lmm/bmf.cpp @@ -0,0 +1,245 @@ +/* Copyright (c) 2007-2022. 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. */ + +#include "src/kernel/lmm/bmf.hpp" +#include +#include +#include + +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(ker_bmf, kernel, "Kernel BMF solver"); + +void simgrid::kernel::lmm::BmfSystem::set_matrix_A() +{ + A_.resize(active_constraint_set.size(), variable_set.size()); + A_.setZero(); + maxA_.resize(active_constraint_set.size(), variable_set.size()); + + int var_idx = 0; + for (Variable& var : variable_set) { + if (var.sharing_penalty_ <= 0) + continue; + bool active = false; + var.value_ = 1; // assign something by default for tasks with 0 consumption + for (const Element& elem : var.cnsts_) { + double consumption = elem.consumption_weight; + if (consumption > 0) { + int cnst_idx = cnst2idx_[elem.constraint]; + A_(cnst_idx, var_idx) = consumption; + maxA_(cnst_idx, var_idx) = elem.max_consumption_weight; + active = true; + } + } + if (active) { + idx2Var_[var_idx] = &var; + var_idx++; + } + } + // resize matrix to active variables only + A_.conservativeResize(Eigen::NoChange_t::NoChange, var_idx); + maxA_.conservativeResize(Eigen::NoChange_t::NoChange, var_idx); +} + +void simgrid::kernel::lmm::BmfSystem::set_vector_C() +{ + C_.resize(active_constraint_set.size()); + cnst2idx_.clear(); + int cnst_idx = 0; + for (const Constraint& cnst : active_constraint_set) { + C_(cnst_idx) = cnst.bound_; + cnst2idx_[&cnst] = cnst_idx; + cnst_idx++; + } +} + +std::unordered_map> +simgrid::kernel::lmm::BmfSystem::get_alloc(const Eigen::VectorXd& fair_sharing) const +{ + std::unordered_map> alloc; + for (int player_idx = 0; player_idx < A_.cols(); player_idx++) { + int selected_resource = -1; + double min_share = 0; + for (int cnst_idx = 0; cnst_idx < A_.rows(); cnst_idx++) { + if (A_(cnst_idx, player_idx) <= 0.0) + continue; + + double share = fair_sharing[cnst_idx] / A_(cnst_idx, player_idx); + if (selected_resource == -1 || double_positive(min_share - share, sg_maxmin_precision)) { + selected_resource = cnst_idx; + min_share = share; + } + } + alloc[selected_resource].push_back(player_idx); + } + return alloc; +} + +void simgrid::kernel::lmm::BmfSystem::set_fair_sharing(const std::unordered_map>& alloc, + const Eigen::VectorXd& rho, Eigen::VectorXd& fair_sharing) const +{ + for (int r = 0; r < fair_sharing.size(); r++) { + auto it = alloc.find(r); + if (it != alloc.end()) { // resource selected by some player, fair share depends on rho + int player = it->second[0]; // equilibrium assures that every player receives the same, use one of them to + // calculate the fair sharing for resource r + fair_sharing[r] = A_(r, player) * rho[player]; + } else { // nobody selects this resource, fair_sharing depends on resource saturation + // resource r is saturated (A[r,*] * rho > C), divide it among players + double consumption_r = A_.row(r) * rho; + double_update(&consumption_r, C_[r], sg_maxmin_precision); + if (consumption_r > 0.0) { + int n_players = std::count_if(A_.row(r).data(), A_.row(r).data() + A_.row(r).size(), + [](double v) { return double_positive(v, sg_maxmin_precision); }); + fair_sharing[r] = C_[r] / n_players; + } else { + fair_sharing[r] = C_[r]; + } + } + } +} + +template std::string simgrid::kernel::lmm::BmfSystem::debug_eigen(const T& obj) const +{ + std::stringstream debug; + debug << obj; + return debug.str(); +} + +template std::string simgrid::kernel::lmm::BmfSystem::debug_vector(const std::vector& vector) const +{ + std::stringstream debug; + std::copy(vector.begin(), vector.end(), std::ostream_iterator(debug, " ")); + return debug.str(); +} + +std::string simgrid::kernel::lmm::BmfSystem::debug_alloc(const std::unordered_map>& alloc) const +{ + std::stringstream debug; + for (const auto& e : alloc) { + debug << "{" + std::to_string(e.first) + ": [" + debug_vector(e.second) + "]}, "; + } + return debug.str(); +} + +Eigen::VectorXd +simgrid::kernel::lmm::BmfSystem::equilibrium(const std::unordered_map>& alloc) const +{ + int n_players = A_.cols(); + Eigen::MatrixXd A_p = Eigen::MatrixXd::Zero(n_players, n_players); // square matrix with number of players + Eigen::VectorXd C_p = Eigen::VectorXd::Zero(n_players); + + // iterate over alloc to verify if 2 players have chosen the same resource + // if so, they must have a fair sharing of this resource, adjust A_p and C_p accordingly + int last_row = n_players - 1; + int first_row = 0; + for (const auto& e : alloc) { + // add one row for the resource with A[r,] + int cur_resource = e.first; + A_p.row(first_row) = A_.row(cur_resource); + C_p[first_row] = C_[cur_resource]; + first_row++; + if (e.second.size() > 1) { + int i = e.second[0]; // first player + for (size_t idx = 1; idx < e.second.size(); idx++) { // for each other player sharing this resource + /* player i and k on this resource j: so maxA_ji*rho_i - maxA_jk*rho_k = 0 */ + int k = e.second[idx]; + C_p[last_row] = 0; + A_p(last_row, i) = maxA_(cur_resource, i); + A_p(last_row, k) = -maxA_(cur_resource, k); + last_row--; + } + } + } + + XBT_DEBUG("A':\n%s", debug_eigen(A_p).c_str()); + + XBT_DEBUG("C':\n%s", debug_eigen(C_p).c_str()); + return Eigen::FullPivLU(A_p).solve(C_p); +} + +bool simgrid::kernel::lmm::BmfSystem::is_bmf(const Eigen::VectorXd& rho) const +{ + bool bmf = true; + + // 1) the capacity of all resources is respected + Eigen::VectorXd remaining = (A_ * rho) - C_; + bmf = bmf && (not std::any_of(remaining.data(), remaining.data() + remaining.size(), + [](double v) { return double_positive(v, sg_maxmin_precision); })); + + // 2) at least 1 resource is saturated + bmf = bmf && (std::any_of(remaining.data(), remaining.data() + remaining.size(), + [](double v) { return double_equals(v, 0.0, sg_maxmin_precision); })); + // 3) every player receives maximum share in at least 1 saturated resource + // due to subflows, compare with the maximum consumption and not the A matrix + Eigen::MatrixXd usage = + maxA_.array().rowwise() * rho.transpose().array(); // usage_ji: indicates the usage of player i on resource j + + XBT_DEBUG("Usage_ji considering max consumption:\n%s", debug_eigen(usage).c_str()); + auto max_share = usage.rowwise().maxCoeff(); // max share for each resource j + + // matrix_ji: boolean indicating player p has the maximum share at resource j + Eigen::MatrixXi player_max_share = + ((usage.array().colwise() - max_share.array()).abs() <= sg_maxmin_precision).cast(); + // but only saturated resources must be considered + Eigen::VectorXi saturated = ((remaining.array().abs() <= sg_maxmin_precision)).cast(); + XBT_DEBUG("Saturated_j resources:\n%s", debug_eigen(saturated).c_str()); + player_max_share.array().colwise() *= saturated.array(); + + XBT_DEBUG("Player_ji usage of saturated resources:\n%s", debug_eigen(player_max_share).c_str()); + // for all columns(players) it has to be the max at least in 1 + bmf = bmf && (player_max_share.colwise().sum().all() >= 1); + return bmf; +} + +void simgrid::kernel::lmm::BmfSystem::bottleneck_solve() +{ + if (not modified_) + return; + + /* initialize players' weight and constraint matrices */ + set_vector_C(); + set_matrix_A(); + XBT_DEBUG("A:\n%s", debug_eigen(A_).c_str()); + XBT_DEBUG("C:\n%s", debug_eigen(C_).c_str()); + + /* no flows to share, just returns */ + if (A_.cols() == 0) + return; + + int it = 0; + auto fair_sharing = C_; + + /* BMF allocation for each player (current and last one) stop when are equal */ + std::unordered_map> last_alloc; + auto cur_alloc = get_alloc(fair_sharing); + Eigen::VectorXd rho; + while (it < max_iteration_ && last_alloc != cur_alloc) { + last_alloc = cur_alloc; + XBT_DEBUG("BMF: iteration %d", it); + XBT_DEBUG("B (current allocation): %s", debug_alloc(cur_alloc).c_str()); + + // solve inv(A)*rho = C + rho = equilibrium(cur_alloc); + XBT_DEBUG("rho:\n%s", debug_eigen(rho).c_str()); + + // get fair sharing for each resource + set_fair_sharing(cur_alloc, rho, fair_sharing); + XBT_DEBUG("Fair sharing vector (per resource):\n%s", debug_eigen(fair_sharing).c_str()); + + // get new allocation for players + cur_alloc = get_alloc(fair_sharing); + XBT_DEBUG("B (new allocation): %s", debug_alloc(cur_alloc).c_str()); + it++; + } + + xbt_assert(is_bmf(rho), "Not a BMF allocation"); + + /* setting rhos */ + for (int i = 0; i < rho.size(); i++) { + idx2Var_[i]->value_ = rho[i]; + } + + XBT_DEBUG("BMF done after %d iterations", it); + print(); +} diff --git a/src/kernel/lmm/bmf.hpp b/src/kernel/lmm/bmf.hpp new file mode 100644 index 0000000000..13b42cc763 --- /dev/null +++ b/src/kernel/lmm/bmf.hpp @@ -0,0 +1,58 @@ +/* Copyright (c) 2004-2022. 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. */ + +#ifndef SURF_BMF_HPP +#define SURF_BMF_HPP + +#include "src/kernel/lmm/maxmin.hpp" +#include + +namespace simgrid { +namespace kernel { +namespace lmm { + +class XBT_PUBLIC BmfSystem : public System { +public: + using System::System; + void solve() final { bottleneck_solve(); } + +private: + void bottleneck_solve(); + void set_matrix_A(); + void set_vector_C(); + std::unordered_map> get_alloc(const Eigen::VectorXd& fair_sharing) const; + Eigen::VectorXd equilibrium(const std::unordered_map>& alloc) const; + + void set_fair_sharing(const std::unordered_map>& alloc, const Eigen::VectorXd& rho, + Eigen::VectorXd& fair_sharing) const; + + template std::string debug_eigen(const T& obj) const; + template std::string debug_vector(const std::vector& vector) const; + std::string debug_alloc(const std::unordered_map>& alloc) const; + /** + * @brief Check if allocation is BMF + * + * To be a bmf allocation it must: + * - respect the capacity of all resources + * - saturate at least 1 resource + * - every player receives maximum share in at least 1 saturated resource + * @param rho Allocation + * @return true if BMF false otherwise + */ + bool is_bmf(const Eigen::VectorXd& rho) const; + + int max_iteration_ = 10; + Eigen::MatrixXd A_; + Eigen::MatrixXd maxA_; + std::unordered_map idx2Var_; + Eigen::VectorXd C_; + std::unordered_map cnst2idx_; +}; + +} // namespace lmm +} // namespace kernel +} // namespace simgrid + +#endif \ No newline at end of file diff --git a/src/kernel/lmm/bmf_test.cpp b/src/kernel/lmm/bmf_test.cpp new file mode 100644 index 0000000000..ef11a691cd --- /dev/null +++ b/src/kernel/lmm/bmf_test.cpp @@ -0,0 +1,314 @@ +/* Copyright (c) 2019-2022. 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. */ + +#include "src/include/catch.hpp" +#include "src/kernel/lmm/bmf.hpp" +#include "src/surf/surf_interface.hpp" +#include "xbt/log.h" + +namespace lmm = simgrid::kernel::lmm; + +TEST_CASE("kernel::bmf Basic tests", "[kernel-bmf-basic]") +{ + lmm::BmfSystem Sys(false); + xbt_log_control_set("ker_bmf.thres:debug"); + + SECTION("Single flow") + { + /* + * A single variable using a single resource + * + * In details: + * o System: a1 * p1 * \rho1 < C + * o consumption_weight: a1=1 + * o sharing_penalty: p1=1 + * + * Expectations + * o rho1 = C + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3); + 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(), 3, sg_maxmin_precision)); + } + + SECTION("Two flows") + { + /* + * Two flows sharing a single resource + * + * In details: + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C + * o consumption_weight: a1=1 ; a2=10 + * o sharing_penalty: p1=1 ; p2=1 + * + * Expectations + * o a1*rho1 = C/2 + * o a2*rho2 = C/2 + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3); + 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, 10); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), 3.0 / 2.0, sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), (3.0 / 2.0) / 10.0, sg_maxmin_precision)); + } + + SECTION("Disable variable doesn't count") + { + /* + * Two flows sharing a single resource, but only disabled + * + * In details: + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C + * o consumption_weight: a1=1 ; a2=10 + * o sharing_penalty: p1=1 ; p2=0 + * + * Expectations + * o a1*rho1 = C + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3); + 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, 10); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), 3.0, sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), 0.0, sg_maxmin_precision)); + } + + SECTION("No consumption variable") + { + /* + * An empty variable, no consumption, just assure it receives something + * + * o System: a1 * p1 * \rho1 < C + * o consumption_weight: a1=0 + * o sharing_penalty: p1=1 + * + * Expectations + * o rho1 > 0 + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3); + 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, 10); + Sys.solve(); + + REQUIRE(double_positive(rho_1->get_value(), sg_maxmin_precision)); + } + + Sys.variable_free_all(); +} + +TEST_CASE("kernel::bmf Advanced tests", "[kernel-bmf-advanced]") +{ + lmm::BmfSystem Sys(false); + xbt_log_control_set("ker_bmf.thres:debug"); + + SECTION("2 flows, 2 resources") + { + /* + * Two flows sharing 2 resources with opposite requirements + * + * In details: + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C1 + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C2 + * o C1 == C2 + * o consumption_weight: a11=1, a12=10, a21=10, a22=1 + * o sharing_penalty: p1=1, p2=1 + * + * Expectations + * o rho1 = rho2 = C/11 + + * Matrices: + * [1 10] * [rho1 rho2] = [1] + * [10 1] [1] + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1); + lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1); + lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 2); + lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 2); + + Sys.expand(sys_cnst, rho_1, 1); + Sys.expand(sys_cnst2, rho_1, 10); + Sys.expand(sys_cnst, rho_2, 10); + Sys.expand(sys_cnst2, rho_2, 1); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), 1.0 / 11.0, sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), 1.0 / 11.0, sg_maxmin_precision)); + } + + SECTION("BMF paper example") + { + /* + * 3 flows sharing 3 resources + * + * In details: + * [1 1 1/2] * [rho1 rho2 rho3] = [1] + * [1 1/2 1 ] [1] + * [1 3/4 3/4] [1] + * + * Expectations (several possible BMF allocations, our algorithm return this) + * o rho1 = rho2 = rho3 = 2/5 + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1); + lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1); + lmm::Constraint* sys_cnst3 = Sys.constraint_new(nullptr, 1); + lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 3); + lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 3); + lmm::Variable* rho_3 = Sys.variable_new(nullptr, 1, -1, 3); + + Sys.expand(sys_cnst3, rho_1, 1.0); // put this expand first to force a singular A' matrix + Sys.expand(sys_cnst, rho_1, 1.0); + Sys.expand(sys_cnst2, rho_1, 1.0); + Sys.expand(sys_cnst, rho_2, 1.0); + Sys.expand(sys_cnst2, rho_2, 1.0 / 2.0); + Sys.expand(sys_cnst3, rho_2, 3.0 / 4.0); + Sys.expand(sys_cnst, rho_3, 1.0 / 2.0); + Sys.expand(sys_cnst2, rho_3, 1.0); + Sys.expand(sys_cnst3, rho_3, 3.0 / 4.0); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), 1.0 / 3.0, sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), 4.0 / 9.0, sg_maxmin_precision)); + REQUIRE(double_equals(rho_3->get_value(), 4.0 / 9.0, sg_maxmin_precision)); + } + + Sys.variable_free_all(); +} + +TEST_CASE("kernel::bmf Subflows", "[kernel-bmf-subflow]") +{ + lmm::BmfSystem Sys(false); + xbt_log_control_set("ker_bmf.thres:debug"); + + SECTION("2 subflows and 1 resource") + { + /* + * 2 identical flows composed of 2 subflows + * + * They must receive the same share and use same amount of resources + * + * In details: + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C + * o consumption_weight: a11=5, a12=7, a2=7, a2=5 + * o sharing_penalty: p1=1, p2=1 + * + * Expectations + * o rho1 = rho2 = (C/2)/12 + + * Matrices: + * [12 12] * [rho1 rho2] = [1] + * [12 12] [0] + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 5); + lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1); + lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1); + + Sys.expand_add(sys_cnst, rho_1, 5); + Sys.expand_add(sys_cnst, rho_1, 7); + Sys.expand_add(sys_cnst, rho_2, 7); + Sys.expand_add(sys_cnst, rho_2, 5); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), 5.0 / 24.0, sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), 5.0 / 24.0, sg_maxmin_precision)); + } + + SECTION("1 subflows, 1 flow and 1 resource") + { + /* + * 2 flows, 1 resource + * 1 flow composed of 2 subflows + * + * Same share/rho, but subflow uses 50% more resources since it has a second connection/subflow + * + * In details: + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C + * o consumption_weight: a11=10, a12=5 a2=10 + * o sharing_penalty: p1=1, p2=1 + * + * Expectations + * o rho1 = (C/25) + * o rho2 = (C/25) + + * Matrices: + * [15 10] * [rho1 rho2] = [1] + * [10 10] [0] + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 5); + lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1); + lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1); + + Sys.expand_add(sys_cnst, rho_1, 10); + Sys.expand_add(sys_cnst, rho_1, 5); + Sys.expand(sys_cnst, rho_2, 10); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), (5.0 / 25.0), sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), (5.0 / 25.0), sg_maxmin_precision)); + REQUIRE(double_equals(15 * rho_1->get_value(), 10 * rho_2->get_value() * 3 / 2, sg_maxmin_precision)); + } + + SECTION("1 subflows using 2 resources: different max for each resource") + { + /* + * Test condition that we may have different max for different resources + * + * In details: + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C + * o consumption_weight: a11=1, a12=1, a2=1 + * o consumption_weight: a21=1/2, a12=1/2 a2=3/2 + * o sharing_penalty: p1=1, p2=1 + * + * Expectations + * o rho1 = (C1/3) + * o rho2 = (C1/3) + + * Matrices: + * [2 1 ] * [rho1 rho2] = [1] + * [1 -1] [0] + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1); + lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1); + lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 2); + lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 2); + + Sys.expand_add(sys_cnst, rho_1, 1.0); + Sys.expand_add(sys_cnst, rho_1, 1.0); + Sys.expand(sys_cnst, rho_2, 1); + Sys.expand_add(sys_cnst2, rho_1, 1.0 / 2.0); + Sys.expand_add(sys_cnst2, rho_1, 1.0 / 2.0); + Sys.expand(sys_cnst2, rho_2, 3.0 / 2.0); + Sys.solve(); + + REQUIRE(double_equals(rho_1->get_value(), (1.0 / 3.0), sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), (1.0 / 3.0), sg_maxmin_precision)); + } + + Sys.variable_free_all(); +} \ No newline at end of file diff --git a/src/kernel/lmm/maxmin.cpp b/src/kernel/lmm/maxmin.cpp index 485159d1d6..69493b6c0c 100644 --- a/src/kernel/lmm/maxmin.cpp +++ b/src/kernel/lmm/maxmin.cpp @@ -240,6 +240,7 @@ void System::expand(Constraint* cnst, Variable* var, double consumption_weight) Element& elem = var->cnsts_.back(); elem.consumption_weight = consumption_weight; + elem.max_consumption_weight = consumption_weight; elem.constraint = cnst; elem.variable = var; @@ -276,6 +277,7 @@ void System::expand_add(Constraint* cnst, Variable* var, double value) if (var->sharing_penalty_ != 0.0) elem.decrease_concurrency(); + elem.max_consumption_weight = std::max(elem.max_consumption_weight, value); if (cnst->sharing_policy_ != Constraint::SharingPolicy::FATPIPE) elem.consumption_weight += value; else diff --git a/src/kernel/lmm/maxmin.hpp b/src/kernel/lmm/maxmin.hpp index be1e401ee1..2aeab539ac 100644 --- a/src/kernel/lmm/maxmin.hpp +++ b/src/kernel/lmm/maxmin.hpp @@ -169,6 +169,8 @@ public: // - if CPU, then probably 1. // - If network, then 1 in forward direction and 0.05 backward for the ACKs double consumption_weight; + // maximum consumption weight (can be different from consumption_weight with subflows/ptasks) + double max_consumption_weight = 0; }; class ConstraintLight { diff --git a/src/surf/ptask_L07.cpp b/src/surf/ptask_L07.cpp index 063fe3170c..b92dedcd0a 100644 --- a/src/surf/ptask_L07.cpp +++ b/src/surf/ptask_L07.cpp @@ -8,6 +8,7 @@ #include #include "src/kernel/EngineImpl.hpp" +#include "src/kernel/lmm/bmf.hpp" #include "src/kernel/resource/profile/Event.hpp" #include "src/surf/ptask_L07.hpp" @@ -23,7 +24,19 @@ void surf_host_model_init_ptask_L07() { XBT_CINFO(xbt_cfg, "Switching to the L07 model to handle parallel tasks."); - auto host_model = std::make_shared("Host_Ptask"); + auto* system = new simgrid::kernel::lmm::FairBottleneck(true /* selective update */); + auto host_model = std::make_shared("Host_Ptask", system); + auto* engine = simgrid::kernel::EngineImpl::get_instance(); + engine->add_model(host_model); + engine->get_netzone_root()->set_host_model(host_model); +} + +void surf_host_model_init_ptask_BMF() +{ + XBT_CINFO(xbt_cfg, "Switching to the BMF model to handle parallel tasks."); + + auto* system = new simgrid::kernel::lmm::BmfSystem(false); + auto host_model = std::make_shared("Host_Ptask", system); auto* engine = simgrid::kernel::EngineImpl::get_instance(); engine->add_model(host_model); engine->get_netzone_root()->set_host_model(host_model); @@ -33,17 +46,16 @@ namespace simgrid { namespace kernel { namespace resource { -HostL07Model::HostL07Model(const std::string& name) : HostModel(name) +HostL07Model::HostL07Model(const std::string& name, lmm::System* sys) : HostModel(name) { - auto* maxmin_system = new lmm::FairBottleneck(true /* selective update */); - set_maxmin_system(maxmin_system); + set_maxmin_system(sys); - auto net_model = std::make_shared("Network_Ptask", this, maxmin_system); + auto net_model = std::make_shared("Network_Ptask", this, sys); auto engine = EngineImpl::get_instance(); engine->add_model(net_model); engine->get_netzone_root()->set_network_model(net_model); - auto cpu_model = std::make_shared("Cpu_Ptask", this, maxmin_system); + auto cpu_model = std::make_shared("Cpu_Ptask", this, sys); engine->add_model(cpu_model); engine->get_netzone_root()->set_cpu_pm_model(cpu_model); } diff --git a/src/surf/ptask_L07.hpp b/src/surf/ptask_L07.hpp index a4336111bd..439c8b41d2 100644 --- a/src/surf/ptask_L07.hpp +++ b/src/surf/ptask_L07.hpp @@ -33,7 +33,7 @@ class XBT_PRIVATE L07Action; *********/ class HostL07Model : public HostModel { public: - explicit HostL07Model(const std::string& name); + HostL07Model(const std::string& name, lmm::System* sys); HostL07Model(const HostL07Model&) = delete; HostL07Model& operator=(const HostL07Model&) = delete; diff --git a/src/surf/surf_interface.cpp b/src/surf/surf_interface.cpp index 2c084bc31f..0e08fa88ac 100644 --- a/src/surf/surf_interface.cpp +++ b/src/surf/surf_interface.cpp @@ -82,6 +82,8 @@ const std::vector surf_host_model_description = { &surf_host_model_init_compound}, {"ptask_L07", "Host model somehow similar to Cas01+CM02 but allowing parallel tasks", &surf_host_model_init_ptask_L07}, + {"ptask_BMF", "Host model which implements BMF resource allocation and allows parallel tasks", + &surf_host_model_init_ptask_BMF}, }; const std::vector surf_optimization_mode_description = { diff --git a/src/surf/surf_interface.hpp b/src/surf/surf_interface.hpp index 2cdc8f4210..95d9acebaf 100644 --- a/src/surf/surf_interface.hpp +++ b/src/surf/surf_interface.hpp @@ -182,6 +182,14 @@ XBT_PUBLIC void surf_host_model_init_current_default(); */ XBT_PUBLIC void surf_host_model_init_ptask_L07(); +/** @ingroup SURF_models + * @brief Initializes the platform with the model BMF + * + * With this model, only parallel tasks can be used. + * Resource sharing is done by calculating a BMF (bottleneck max fairness) allocation + */ +XBT_PUBLIC void surf_host_model_init_ptask_BMF(); + XBT_PUBLIC void surf_disk_model_init_default(); /* -------------------- diff --git a/tools/cmake/DefinePackages.cmake b/tools/cmake/DefinePackages.cmake index a78c0a6369..b48ea4bdff 100644 --- a/tools/cmake/DefinePackages.cmake +++ b/tools/cmake/DefinePackages.cmake @@ -292,6 +292,8 @@ set(NS3_SRC src/surf/network_ns3.cpp src/surf/ns3/ns3_simulator.cpp ) set(SURF_SRC + src/kernel/lmm/bmf.hpp + src/kernel/lmm/bmf.cpp src/kernel/lmm/fair_bottleneck.cpp src/kernel/lmm/maxmin.hpp src/kernel/lmm/maxmin.cpp diff --git a/tools/cmake/Tests.cmake b/tools/cmake/Tests.cmake index 2de92e5df9..b6108a626e 100644 --- a/tools/cmake/Tests.cmake +++ b/tools/cmake/Tests.cmake @@ -138,7 +138,8 @@ set(UNIT_TESTS src/xbt/unit-tests_main.cpp src/xbt/dynar_test.cpp src/xbt/random_test.cpp src/xbt/xbt_str_test.cpp - src/kernel/lmm/maxmin_test.cpp) + src/kernel/lmm/bmf_test.cpp + src/kernel/lmm/maxmin_test.cpp) if (SIMGRID_HAVE_MC) set(UNIT_TESTS ${UNIT_TESTS} src/mc/sosp/Snapshot_test.cpp src/mc/sosp/PageStore_test.cpp) else()