Add non-linear constraints to links.
Similar to disks, user can use a callback to dynamically change the
link's capacity based on the number of flows sharing it.
Only traffic explicit generated by the user is considered as active
flow, so cross-traffic communications aren't taken into account.
Extend s4u::Link::sharing_policy
- New type: NONLINEAR
Example: examples/cpp/network-nonlinear/
include examples/cpp/mc-failing-assert/s4u-mc-failing-assert.tesh
include examples/cpp/network-factors/s4u-network-factors.cpp
include examples/cpp/network-factors/s4u-network-factors.tesh
+include examples/cpp/network-nonlinear/s4u-network-nonlinear.cpp
+include examples/cpp/network-nonlinear/s4u-network-nonlinear.tesh
include examples/cpp/network-ns3-wifi/s4u-network-ns3-wifi.cpp
include examples/cpp/network-ns3-wifi/s4u-network-ns3-wifi.tesh
include examples/cpp/network-ns3/3hosts_2links_d.xml
replay-comm replay-io
routing-get-clusters
synchro-barrier synchro-condition-variable synchro-condition-variable-waituntil synchro-mutex synchro-semaphore
- clusters-multicpu network-factors)
+ clusters-multicpu network-factors network-nonlinear)
# Use default source file unless specified otherwise
if(NOT DEFINED _${example}_sources)
* @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)
+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>;
* @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)
+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);
--- /dev/null
+/* Copyright (c) 2010-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
+ * network links.
+ */
+
+#include <simgrid/s4u.hpp>
+
+namespace sg4 = simgrid::s4u;
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_network_nonlinear, "Messages specific for this s4u example");
+
+/*************************************************************************************************/
+class Sender {
+ int messages_count; /* - number of messages */
+ int msg_size = 1e6; /* - message size in bytes */
+
+public:
+ explicit Sender(int count) : messages_count(count) {}
+ void operator()() const
+ {
+ // sphinx-doc: init-begin (this line helps the doc to build; ignore it)
+ /* Vector in which we store all ongoing communications */
+ std::vector<sg4::CommPtr> pending_comms;
+
+ /* Make a vector of the mailboxes to use */
+ sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver");
+ // sphinx-doc: init-end
+
+ /* Start dispatching all messages to receiver */
+ for (int i = 0; i < messages_count; i++) {
+ std::string msg_content = std::string("Message ") + std::to_string(i);
+ // Copy the data we send: the 'msg_content' variable is not a stable storage location.
+ // It will be destroyed when this actor leaves the loop, ie before the receiver gets it
+ auto* payload = new std::string(msg_content);
+ unsigned long long size = msg_size * (i + 1);
+ XBT_INFO("Send '%s' to '%s, msg size: %llu'", msg_content.c_str(), mbox->get_cname(), size);
+
+ /* Create a communication representing the ongoing communication, and store it in pending_comms */
+ sg4::CommPtr comm = mbox->put_async(payload, size);
+ pending_comms.push_back(comm);
+ }
+
+ XBT_INFO("Done dispatching all messages");
+
+ /* Now that all message exchanges were initiated, wait for their completion in one single call */
+ sg4::Comm::wait_all(pending_comms);
+ // sphinx-doc: put-end
+
+ XBT_INFO("Goodbye now!");
+ }
+};
+
+/* Receiver actor expects 1 argument: number of messages to be received */
+class Receiver {
+ sg4::Mailbox* mbox;
+ int messages_count = 10; /* - number of messages */
+
+public:
+ explicit Receiver(int count) : messages_count(count) { mbox = sg4::Mailbox::by_name("receiver"); }
+ void operator()()
+ {
+ /* Vector in which we store all incoming msgs */
+ std::vector<std::unique_ptr<std::string*>> pending_msgs;
+ std::vector<sg4::CommPtr> pending_comms;
+
+ XBT_INFO("Wait for %d messages asynchronously", messages_count);
+ for (int i = 0; i < messages_count; i++) {
+ pending_msgs.push_back(std::make_unique<std::string*>());
+ pending_comms.emplace_back(mbox->get_async<std::string>(pending_msgs[i].get()));
+ }
+ while (not pending_comms.empty()) {
+ ssize_t index = sg4::Comm::wait_any(pending_comms);
+ std::string* msg = *pending_msgs[index];
+ XBT_INFO("I got '%s'.", msg->c_str());
+ /* cleanup memory and remove from vectors */
+ delete msg;
+ pending_comms.erase(pending_comms.begin() + index);
+ pending_msgs.erase(pending_msgs.begin() + index);
+ }
+ }
+};
+
+/*************************************************************************************************/
+/** @brief Non-linear resource sharing for links
+ *
+ * Note that the callback is called twice in this example:
+ * 1) link UP: with the number of active flows (from 9 to 1)
+ * 2) link DOWN: with 0 active flows. A crosstraffic communication is happing
+ * in the down link, but it's not considered as an active flow.
+ */
+static double link_nonlinear(const sg4::Link* link, double capacity, int n)
+{
+ /* emulates a degradation in link according to the number of flows
+ * you probably want something more complex than that and based on real
+ * experiments */
+ capacity = std::min(capacity, capacity * (1.0 - (n - 1) / 10.0));
+ XBT_INFO("Link %s, %d active communications, new capacity %lf", link->get_cname(), n, capacity);
+ return capacity;
+}
+
+/** @brief Create a simple 2-hosts platform */
+static void load_platform()
+{
+ /* Creates the platform
+ * ________ __________
+ * | Sender |===============| Receiver |
+ * |________| Link1 |__________|
+ */
+ auto* zone = sg4::create_full_zone("Zone1");
+ auto* sender = zone->create_host("sender", 1)->seal();
+ auto* receiver = zone->create_host("receiver", 1)->seal();
+
+ auto* link = zone->create_split_duplex_link("link1", 1e6);
+ /* setting same callbacks (could be different) for link UP/DOWN in split-duplex link */
+ link->get_link_up()->set_sharing_policy(
+ sg4::Link::SharingPolicy::NONLINEAR,
+ std::bind(&link_nonlinear, link->get_link_up(), std::placeholders::_1, std::placeholders::_2));
+ link->get_link_down()->set_sharing_policy(
+ sg4::Link::SharingPolicy::NONLINEAR,
+ std::bind(&link_nonlinear, link->get_link_down(), std::placeholders::_1, std::placeholders::_2));
+ link->set_latency(10e-6)->seal();
+
+ /* create routes between nodes */
+ zone->add_route(sender->get_netpoint(), receiver->get_netpoint(), nullptr, nullptr,
+ {{link, sg4::LinkInRoute::Direction::UP}}, true);
+ zone->seal();
+
+ /* create actors Sender/Receiver */
+ sg4::Actor::create("receiver", receiver, Receiver(9));
+ sg4::Actor::create("sender", sender, Sender(9));
+}
+
+/*************************************************************************************************/
+int main(int argc, char* argv[])
+{
+ sg4::Engine e(&argc, argv);
+
+ /* create platform */
+ load_platform();
+
+ /* runs the simulation */
+ e.run();
+
+ return 0;
+}
--- /dev/null
+$ ${bindir:=.}/s4u-network-nonlinear "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n"
+>[ 0.000000] (1:receiver@receiver) Wait for 9 messages asynchronously
+>[ 0.000000] (2:sender@sender) Send 'Message 0' to 'receiver, msg size: 1000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 1' to 'receiver, msg size: 2000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 2' to 'receiver, msg size: 3000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 3' to 'receiver, msg size: 4000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 4' to 'receiver, msg size: 5000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 5' to 'receiver, msg size: 6000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 6' to 'receiver, msg size: 7000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 7' to 'receiver, msg size: 8000000'
+>[ 0.000000] (2:sender@sender) Send 'Message 8' to 'receiver, msg size: 9000000'
+>[ 0.000000] (2:sender@sender) Done dispatching all messages
+>[ 0.000000] (0:maestro@) Link link1_UP, 0 active communications, new capacity 1000000.000000
+>[ 0.000000] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[ 0.000130] (0:maestro@) Link link1_UP, 9 active communications, new capacity 200000.000000
+>[ 0.000130] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[ 46.391883] (1:receiver@receiver) I got 'Message 0'.
+>[ 46.391883] (0:maestro@) Link link1_UP, 8 active communications, new capacity 300000.000000
+>[ 46.391883] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[ 73.883292] (1:receiver@receiver) I got 'Message 1'.
+>[ 73.883292] (0:maestro@) Link link1_UP, 7 active communications, new capacity 400000.000000
+>[ 73.883292] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[ 91.924529] (1:receiver@receiver) I got 'Message 2'.
+>[ 91.924529] (0:maestro@) Link link1_UP, 6 active communications, new capacity 500000.000000
+>[ 91.924529] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[104.295663] (1:receiver@receiver) I got 'Message 3'.
+>[104.295663] (0:maestro@) Link link1_UP, 5 active communications, new capacity 600000.000000
+>[104.295663] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[112.886728] (1:receiver@receiver) I got 'Message 4'.
+>[112.886728] (0:maestro@) Link link1_UP, 4 active communications, new capacity 700000.000000
+>[112.886728] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[118.777744] (1:receiver@receiver) I got 'Message 5'.
+>[118.777744] (0:maestro@) Link link1_UP, 3 active communications, new capacity 800000.000000
+>[118.777744] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[122.643724] (1:receiver@receiver) I got 'Message 6'.
+>[122.643724] (0:maestro@) Link link1_UP, 2 active communications, new capacity 900000.000000
+>[122.643724] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[124.934674] (1:receiver@receiver) I got 'Message 7'.
+>[124.934674] (0:maestro@) Link link1_UP, 1 active communications, new capacity 1000000.000000
+>[124.934674] (0:maestro@) Link link1_DOWN, 0 active communications, new capacity 1000000.000000
+>[125.965602] (1:receiver@receiver) I got 'Message 8'.
+>[125.965602] (2:sender@sender) Goodbye now!
kernel::resource::LinkImplIntf* const pimpl_;
public:
- enum class SharingPolicy { WIFI = 3, SPLITDUPLEX = 2, SHARED = 1, FATPIPE = 0 };
+ enum class SharingPolicy { NONLINEAR = 4, WIFI = 3, SPLITDUPLEX = 2, SHARED = 1, FATPIPE = 0 };
kernel::resource::LinkImpl* get_impl() const;
Link* set_latency(const std::string& value);
/** @brief Describes how the link is shared between flows */
- Link* set_sharing_policy(SharingPolicy policy);
+ Link* set_sharing_policy(SharingPolicy policy, const NonLinearResourceCb& cb = {});
SharingPolicy get_sharing_policy() const;
/** Setup the profile with states events (ON or OFF). The profile must contain boolean values. */
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;
}
"WIFI netzone %s contains more than one link. Please only declare one, the wifi link.", get_cname());
wifi_link_ = get_network_model()->create_wifi_link(name, bandwidths);
- wifi_link_->set_sharing_policy(s4u::Link::SharingPolicy::WIFI);
+ wifi_link_->set_sharing_policy(s4u::Link::SharingPolicy::WIFI, {});
return wifi_link_->get_iface();
}
} // namespace routing
return this;
}
-Link* Link::set_sharing_policy(Link::SharingPolicy policy)
+Link* Link::set_sharing_policy(Link::SharingPolicy policy, const NonLinearResourceCb& cb)
{
if (policy == SharingPolicy::SPLITDUPLEX)
throw std::invalid_argument(std::string("Impossible to set split-duplex for the link: ") + get_name() +
std::string(". Use NetZone::create_split_duplex_link."));
- kernel::actor::simcall([this, policy] { pimpl_->set_sharing_policy(policy); });
+ kernel::actor::simcall([this, policy, &cb] { pimpl_->set_sharing_policy(policy, cb); });
return this;
}
Link::SharingPolicy Link::get_sharing_policy() const
return get_model()->get_maxmin_system()->constraint_used(get_constraint());
}
-void LinkImpl::set_sharing_policy(s4u::Link::SharingPolicy policy)
+constexpr kernel::lmm::Constraint::SharingPolicy to_maxmin_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, {});
+ switch (policy) {
+ case s4u::Link::SharingPolicy::NONLINEAR:
+ return kernel::lmm::Constraint::SharingPolicy::NONLINEAR;
+ case s4u::Link::SharingPolicy::FATPIPE:
+ return kernel::lmm::Constraint::SharingPolicy::FATPIPE;
+ default:
+ return kernel::lmm::Constraint::SharingPolicy::SHARED;
+ }
+}
+
+void LinkImpl::set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb)
+{
+ get_constraint()->set_sharing_policy(to_maxmin_policy(policy), cb);
sharing_policy_ = policy;
}
+
s4u::Link::SharingPolicy LinkImpl::get_sharing_policy() const
{
return sharing_policy_;
LinkImpl& operator=(const LinkImpl&) = delete;
~LinkImpl() override = default; // Use destroy() instead of this destructor.
+ Metric latency_ = {0.0, 1, nullptr};
+ Metric bandwidth_ = {1.0, 1, nullptr};
+
public:
void destroy(); // Must be called instead of the destructor
double get_latency() const override { return latency_.peak * latency_.scale; }
/** @brief The sharing policy */
- void set_sharing_policy(s4u::Link::SharingPolicy policy) override;
+ void set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb) override;
s4u::Link::SharingPolicy get_sharing_policy() const override;
/** @brief Check if the Link is used */
void set_latency_profile(kernel::profile::Profile* profile) override;
void set_concurrency_limit(int limit) const override;
-
- Metric latency_ = {0.0, 1, nullptr};
- Metric bandwidth_ = {1.0, 1, nullptr};
};
} // namespace resource
virtual void set_latency(double value) = 0;
/** @brief The sharing policy */
- virtual void set_sharing_policy(s4u::Link::SharingPolicy policy) = 0;
+ virtual void set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb) = 0;
virtual s4u::Link::SharingPolicy get_sharing_policy() const = 0;
/* setup the profile file with bandwidth events (peak speed changes due to external load).
xbt_die("Impossible to call is_used() in split-duplex links. Call it for each individual link.");
}
-void SplitDuplexLinkImpl::set_sharing_policy(s4u::Link::SharingPolicy policy)
+void SplitDuplexLinkImpl::set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb)
{
xbt_die("Impossible to change sharing policy of split-duplex links");
}
void set_latency(double value) override;
/** @brief The sharing policy */
- void set_sharing_policy(s4u::Link::SharingPolicy policy) override;
+ void set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb) override;
s4u::Link::SharingPolicy get_sharing_policy() const override { return s4u::Link::SharingPolicy::SPLITDUPLEX; }
/** @brief Get link composing this split-duplex link */
set_maxmin_system(new lmm::System(select));
loopback_ = create_link("__loopback__", {config::get_value<double>("network/loopback-bw")});
- loopback_->set_sharing_policy(s4u::Link::SharingPolicy::FATPIPE);
+ loopback_->set_sharing_policy(s4u::Link::SharingPolicy::FATPIPE, {});
loopback_->set_latency(config::get_value<double>("network/loopback-lat"));
loopback_->seal();
}
LinkImpl* NetworkNS3Model::create_wifi_link(const std::string& name, const std::vector<double>& bandwidths)
{
auto* link = create_link(name, bandwidths);
- link->set_sharing_policy(s4u::Link::SharingPolicy::WIFI);
+ link->set_sharing_policy(s4u::Link::SharingPolicy::WIFI, {});
return link;
}
latency_.peak = latency;
}
-void LinkNS3::set_sharing_policy(s4u::Link::SharingPolicy policy)
+void LinkNS3::set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb)
{
sharing_policy_ = policy;
}
void set_latency(double) override;
void set_bandwidth_profile(profile::Profile* profile) override;
void set_latency_profile(profile::Profile* profile) override;
- void set_sharing_policy(s4u::Link::SharingPolicy policy) override;
+ void set_sharing_policy(s4u::Link::SharingPolicy policy, const s4u::NonLinearResourceCb& cb) override;
s4u::Link::SharingPolicy get_sharing_policy() const override { return sharing_policy_; }
};
{
set_maxmin_system(sys);
loopback_ = create_link("__loopback__", {simgrid::config::get_value<double>("network/loopback-bw")});
- loopback_->set_sharing_policy(s4u::Link::SharingPolicy::FATPIPE);
+ loopback_->set_sharing_policy(s4u::Link::SharingPolicy::FATPIPE, {});
loopback_->set_latency(simgrid::config::get_value<double>("network/loopback-lat"));
loopback_->seal();
}