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)
--- /dev/null
+/* 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 <eigen3/Eigen/LU>
+#include <iostream>
+#include <sstream>
+
+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<int, std::vector<int>>
+simgrid::kernel::lmm::BmfSystem::get_alloc(const Eigen::VectorXd& fair_sharing) const
+{
+ std::unordered_map<int, std::vector<int>> 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<int, std::vector<int>>& 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 <typename T> std::string simgrid::kernel::lmm::BmfSystem::debug_eigen(const T& obj) const
+{
+ std::stringstream debug;
+ debug << obj;
+ return debug.str();
+}
+
+template <typename T> std::string simgrid::kernel::lmm::BmfSystem::debug_vector(const std::vector<T>& vector) const
+{
+ std::stringstream debug;
+ std::copy(vector.begin(), vector.end(), std::ostream_iterator<T>(debug, " "));
+ return debug.str();
+}
+
+std::string simgrid::kernel::lmm::BmfSystem::debug_alloc(const std::unordered_map<int, std::vector<int>>& 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<int, std::vector<int>>& 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<Eigen::MatrixXd>(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<int>();
+ // but only saturated resources must be considered
+ Eigen::VectorXi saturated = ((remaining.array().abs() <= sg_maxmin_precision)).cast<int>();
+ 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<int, std::vector<int>> 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();
+}
--- /dev/null
+/* 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 <eigen3/Eigen/Dense>
+
+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<int, std::vector<int>> get_alloc(const Eigen::VectorXd& fair_sharing) const;
+ Eigen::VectorXd equilibrium(const std::unordered_map<int, std::vector<int>>& alloc) const;
+
+ void set_fair_sharing(const std::unordered_map<int, std::vector<int>>& alloc, const Eigen::VectorXd& rho,
+ Eigen::VectorXd& fair_sharing) const;
+
+ template <typename T> std::string debug_eigen(const T& obj) const;
+ template <typename T> std::string debug_vector(const std::vector<T>& vector) const;
+ std::string debug_alloc(const std::unordered_map<int, std::vector<int>>& 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<int, Variable*> idx2Var_;
+ Eigen::VectorXd C_;
+ std::unordered_map<const Constraint*, int> cnst2idx_;
+};
+
+} // namespace lmm
+} // namespace kernel
+} // namespace simgrid
+
+#endif
\ No newline at end of file
--- /dev/null
+/* 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
Element& elem = var->cnsts_.back();
elem.consumption_weight = consumption_weight;
+ elem.max_consumption_weight = consumption_weight;
elem.constraint = cnst;
elem.variable = var;
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
// - 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 {
#include <xbt/config.hpp>
#include "src/kernel/EngineImpl.hpp"
+#include "src/kernel/lmm/bmf.hpp"
#include "src/kernel/resource/profile/Event.hpp"
#include "src/surf/ptask_L07.hpp"
{
XBT_CINFO(xbt_cfg, "Switching to the L07 model to handle parallel tasks.");
- auto host_model = std::make_shared<simgrid::kernel::resource::HostL07Model>("Host_Ptask");
+ auto* system = new simgrid::kernel::lmm::FairBottleneck(true /* selective update */);
+ auto host_model = std::make_shared<simgrid::kernel::resource::HostL07Model>("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<simgrid::kernel::resource::HostL07Model>("Host_Ptask", system);
auto* engine = simgrid::kernel::EngineImpl::get_instance();
engine->add_model(host_model);
engine->get_netzone_root()->set_host_model(host_model);
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<NetworkL07Model>("Network_Ptask", this, maxmin_system);
+ auto net_model = std::make_shared<NetworkL07Model>("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<CpuL07Model>("Cpu_Ptask", this, maxmin_system);
+ auto cpu_model = std::make_shared<CpuL07Model>("Cpu_Ptask", this, sys);
engine->add_model(cpu_model);
engine->get_netzone_root()->set_cpu_pm_model(cpu_model);
}
*********/
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;
&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_model_description_t> surf_optimization_mode_description = {
*/
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();
/* --------------------
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
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()