From e116532f2474ef6a8a4a1a66b66fbdf0d17631a8 Mon Sep 17 00:00:00 2001 From: Martin Quinson Date: Thu, 6 Apr 2023 16:28:33 +0200 Subject: [PATCH] Allow ns3 to be idempotent if correctly patched --- docs/source/Models.rst | 27 ++++---- src/internal_config.h.in | 2 + src/kernel/EngineImpl.cpp | 4 +- src/kernel/resource/models/network_ns3.cpp | 62 +++++++++++++++++-- src/kernel/resource/models/network_ns3.hpp | 2 +- .../ns3-from-src-to-itself.tesh | 2 +- tools/cmake/Modules/FindNS3.cmake | 12 +++- tools/cmake/test_prog/prog_ns3.cpp | 7 +++ 8 files changed, 96 insertions(+), 22 deletions(-) create mode 100644 tools/cmake/test_prog/prog_ns3.cpp diff --git a/docs/source/Models.rst b/docs/source/Models.rst index 9b54d2d15e..1688d80e6b 100644 --- a/docs/source/Models.rst +++ b/docs/source/Models.rst @@ -332,21 +332,26 @@ distributed algorithms such as leader election or causal broadcast. ns-3 as a SimGrid model *********************** -The **ns-3 based model** is the most accurate network model in SimGrid. It relies on the well-known -`ns-3 packet-level network simulator `_ to compute full timing information related to network -transfers. This -model is much slower than the LMM-based models. This is because ns-3 simulates the movement of every network packet involved in -every communication, while the LMM-based models only recompute the respective instantaneous speeds of the currently ongoing -communications when a communication starts or stops. +The **ns-3 based model** is the most accurate network model in SimGrid. It relies on the well-known `ns-3 packet-level network +simulator `_ to compute full timing information related to network transfers. This model is much slower +than the LMM-based models. This is because ns-3 simulates the movement of every network packet involved in every communication, +while the LMM-based models only recompute the respective instantaneous speeds of the currently ongoing communications when a +communication starts or stops. In other terms, both SimGrid and ns-3 are fast and highly optimized, but while SimGrid only +depends on application-level events (starting and stoping of communications), ns-3 depends on network-level events (sending a +packet). You need to install ns-3 and recompile SimGrid accordingly to use this model. -The SimGrid/ns-3 binding only contains features that are common to both systems. Not all ns-3 models are available from -SimGrid (only the TCP and WiFi ones are), while not all SimGrid platform files can be used in conjunction with ns-3 -(routes must be of length 1). Note also that the platform built in ns-3 from the SimGrid -description is very basic. Finally, communicating from a host to -itself is forbidden in ns-3, so every such communication is simulated to take zero time. +The SimGrid/ns-3 binding only contains features that are common to both systems. Not all ns-3 models are available from SimGrid +(only the TCP and WiFi ones are), while not all SimGrid platform files can be used in conjunction with ns-3 (routes must be of +length 1). Note also that the platform built in ns-3 from the SimGrid description is very basic. Finally, communicating from a +host to itself is forbidden in ns-3, so every such communication is simulated to take zero time. +By default, the ns-3 model in SimGrid is not idempotent, unless you patch your version of ns-3 with [this +patch](https://gitlab.com/nsnam/ns-3-dev/-/merge_requests/1338). It is perfectly OK to have a non-idempotent model in SimGrid as +long as you only have only one such model, and as long as you don't use utterly advanced things in SimGrid. If you do want to +have an idempotent ns-3, apply the previously mentioned patch, and recompile SimGrid. It should detect the patch and react +accordingly. Compiling the ns-3/SimGrid binding ================================== diff --git a/src/internal_config.h.in b/src/internal_config.h.in index e13c6ffa7d..b2ecf6efcb 100644 --- a/src/internal_config.h.in +++ b/src/internal_config.h.in @@ -95,3 +95,5 @@ /* The boost_stacktrace_backtrace library */ #cmakedefine01 HAVE_BOOST_STACKTRACE_BACKTRACE /* preferred */ #cmakedefine01 HAVE_BOOST_STACKTRACE_ADDR2LINE /* fallback */ +/* Whether the ns-3 library has GetNextEventTime */ +#cmakedefine01 SIMGRID_HAVE_NS3_GetNextEventTime diff --git a/src/kernel/EngineImpl.cpp b/src/kernel/EngineImpl.cpp index 2404648864..377f2b5795 100644 --- a/src/kernel/EngineImpl.cpp +++ b/src/kernel/EngineImpl.cpp @@ -512,9 +512,9 @@ double EngineImpl::solve(double max_date) const XBT_DEBUG("Looking for next event in all models"); for (auto model : models_) { - if (not model->next_occurring_event_is_idempotent()) { + if (not model->next_occurring_event_is_idempotent()) continue; - } + double next_event = model->next_occurring_event(now_); if ((time_delta < 0.0 || next_event < time_delta) && next_event >= 0.0) { time_delta = next_event; diff --git a/src/kernel/resource/models/network_ns3.cpp b/src/kernel/resource/models/network_ns3.cpp index 423ed3c8f8..28519c88f8 100644 --- a/src/kernel/resource/models/network_ns3.cpp +++ b/src/kernel/resource/models/network_ns3.cpp @@ -310,6 +310,7 @@ static simgrid::config::Flag ns3_seed( namespace simgrid { namespace kernel::resource { +static bool ns3_is_initialized = false; NetworkNS3Model::NetworkNS3Model(const std::string& name) : NetworkModel(name) { @@ -343,6 +344,7 @@ NetworkNS3Model::NetworkNS3Model(const std::string& name) : NetworkModel(name) ns3::GlobalRouteManager::DeleteGlobalRoutes(); // just in case this callback is called twice ns3::GlobalRouteManager::BuildGlobalRoutingDatabase(); ns3::GlobalRouteManager::InitializeRoutes(); + ns3_is_initialized = true; }); routing::on_cluster_creation.connect(&clusterCreation_cb); routing::NetZoneImpl::on_route_creation.connect(&routeCreation_cb); @@ -377,6 +379,31 @@ Action* NetworkNS3Model::communicate(s4u::Host* src, s4u::Host* dst, double size return new NetworkNS3Action(this, size, src, dst); } +#if SIMGRID_HAVE_NS3_GetNextEventTime +/* If patched, ns3 is idempotent and nice to use */ +bool NetworkNS3Model::next_occurring_event_is_idempotent() +{ + return true; +} + +double NetworkNS3Model::next_occurring_event(double sg_time) +{ + if (get_started_action_set()->empty()) { + return -1.0; + } + + double ns3_time = ns3::Simulator::GetNextEventTime().GetSeconds(); + XBT_DEBUG("NS3 tells that the next occuring event is at %f (it's %f in simgrid), so NS3 returns a delta of %f.", + ns3_time, sg_time, ns3_time - sg_time); + return ns3_time - sg_time; +} +#else +/* NS3 is only idempotent with the appropriate patch */ +bool NetworkNS3Model::next_occurring_event_is_idempotent() +{ + return false; +} + double NetworkNS3Model::next_occurring_event(double now) { double time_to_next_flow_completion = 0.0; @@ -406,11 +433,30 @@ double NetworkNS3Model::next_occurring_event(double now) return time_to_next_flow_completion; } +#endif void NetworkNS3Model::update_actions_state(double now, double delta) { static std::vector socket_to_destroy; +#if SIMGRID_HAVE_NS3_GetNextEventTime + /* If the ns-3 model is idempotent, it won't get updated in next_occurring_event() */ + + if (not ns3_is_initialized) { + XBT_DEBUG("PRESOLVE, I SEE YOU. Don't run ns3 until after all initializations are done."); + return; + } + + if (delta >= 0) { + XBT_DEBUG("DO START simulator delta: %f (current simgrid time: %f; current ns3 time: %f)", delta, + simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds()); + ns3_simulator(delta); + } else { + XBT_DEBUG("don't start simulator delta: %f (current simgrid time: %f; current ns3 time: %f)", delta, + simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds()); + } +#endif + for (const auto& [ns3_socket, sgFlow] : flow_from_sock) { NetworkNS3Action* action = sgFlow->action_; XBT_DEBUG("Processing flow %p (socket %s, action %p)", sgFlow, ns3_socket.c_str(), action); @@ -506,7 +552,7 @@ NetworkNS3Action::NetworkNS3Action(Model* model, double totalBytes, s4u::Host* s if (src == dst) { if (static bool warned = false; not warned) { XBT_WARN("Sending from a host %s to itself is not supported by ns-3. Every such communication finishes " - "immediately upon startup.", + "immediately upon startup in the SimGrid+ns-3 system.", src->get_cname()); warned = true; } @@ -579,17 +625,21 @@ ns3::Ptr get_ns3node_from_sghost(const simgrid::s4u::Host* host) } } // namespace simgrid -void ns3_simulator(double maxSeconds) +void ns3_simulator(double maxSeconds) // maxSecond is a delay, not an absolute time { ns3::EventId id; - if (maxSeconds > 0.0) // If there is a maximum amount of time to run + if (maxSeconds >= 0.0) // If there is a maximum amount of time to run id = ns3::Simulator::Schedule(ns3::Seconds(maxSeconds), &ns3::Simulator::Stop); - XBT_DEBUG("Start simulator for at most %fs (current time: %f)", maxSeconds, simgrid::kernel::EngineImpl::get_clock()); + XBT_DEBUG("Start simulator for at most %fs (current simgrid time: %f; current ns3 time: %f)", maxSeconds, + simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds()); +#if SIMGRID_HAVE_NS3_GetNextEventTime + xbt_assert(maxSeconds >= 0.0); +#endif ns3::Simulator::Run(); - XBT_DEBUG("Simulator stopped at %fs", ns3::Simulator::Now().GetSeconds()); + XBT_DEBUG("ns3 simulator stopped at %fs", ns3::Simulator::Now().GetSeconds()); - if (maxSeconds > 0.0) + if (maxSeconds >= 0.0) id.Cancel(); } diff --git a/src/kernel/resource/models/network_ns3.hpp b/src/kernel/resource/models/network_ns3.hpp index 9ec2c4ba31..d536282294 100644 --- a/src/kernel/resource/models/network_ns3.hpp +++ b/src/kernel/resource/models/network_ns3.hpp @@ -22,7 +22,7 @@ public: StandardLinkImpl* create_wifi_link(const std::string& name, const std::vector& bandwidth) override; Action* communicate(s4u::Host* src, s4u::Host* dst, double size, double rate, bool streamed) override; double next_occurring_event(double now) override; - bool next_occurring_event_is_idempotent() override { return false; } + bool next_occurring_event_is_idempotent() override; void update_actions_state(double now, double delta) override; }; diff --git a/teshsuite/s4u/ns3-from-src-to-itself/ns3-from-src-to-itself.tesh b/teshsuite/s4u/ns3-from-src-to-itself/ns3-from-src-to-itself.tesh index ff4e558d78..b6f9251049 100644 --- a/teshsuite/s4u/ns3-from-src-to-itself/ns3-from-src-to-itself.tesh +++ b/teshsuite/s4u/ns3-from-src-to-itself/ns3-from-src-to-itself.tesh @@ -3,6 +3,6 @@ p We just want to check that the ns-3 bindings of SimGrid are working correctly, $ ./ns3-from-src-to-itself ${platfdir}/ns3-big-cluster.xml --cfg=network/model:ns-3 "--log=root.fmt:[%h:%a(%i)]%e[%c/%p]%e%m%n" > [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3' -> [:maestro(0)] [res_ns3/WARNING] Sending from a host c-01.rennes to itself is not supported by ns-3. Every such communication finishes immediately upon startup. +> [:maestro(0)] [res_ns3/WARNING] Sending from a host c-01.rennes to itself is not supported by ns-3. Every such communication finishes immediately upon startup in the SimGrid+ns-3 system. > [c-01.rennes:receiver(1)] [s4u_test/INFO] Done receiving from 2 senders, each of them sending 5 messages diff --git a/tools/cmake/Modules/FindNS3.cmake b/tools/cmake/Modules/FindNS3.cmake index 7154c2bb3b..12722f9591 100644 --- a/tools/cmake/Modules/FindNS3.cmake +++ b/tools/cmake/Modules/FindNS3.cmake @@ -126,8 +126,18 @@ else() endforeach() endif() +set(SIMGRID_HAVE_NS3_GetNextEventTime FALSE) if(SIMGRID_HAVE_NS3) - message(STATUS "ns-3 found (v${NS3_VERSION}; minor:${NS3_MINOR_VERSION}; patch:${NS3_PATCH_VERSION}; libpath: ${NS3_LIBRARY_PATH}).") + try_compile(compile_ns3 ${CMAKE_BINARY_DIR} ${CMAKE_HOME_DIRECTORY}/tools/cmake/test_prog/prog_ns3.cpp + LINK_LIBRARIES "${NS3_LIBRARIES}" + OUTPUT_VARIABLE compile_ns3_output) + if(NOT compile_ns3) + message(STATUS "ns-3 does not have the ns3::Simulator::GetNextEventTime patch. The ns-3 model will not be idempotent. Compilation output:\n ${compile_ns3_output}") + else() + set(SIMGRID_HAVE_NS3_GetNextEventTime TRUE) + endif() + file(REMOVE ${CMAKE_BINARY_DIR}/prog_ns3) + message(STATUS "ns-3 found (v${NS3_VERSION}; minor ver:${NS3_MINOR_VERSION}; patch ver:${NS3_PATCH_VERSION}; GetNextEventTime patch: ${SIMGRID_HAVE_NS3_GetNextEventTime}; libpath: ${NS3_LIBRARY_PATH}).") link_directories(${NS3_LIBRARY_PATH}) include_directories(${NS3_INCLUDE_DIR}) else() diff --git a/tools/cmake/test_prog/prog_ns3.cpp b/tools/cmake/test_prog/prog_ns3.cpp new file mode 100644 index 0000000000..c763cbdcf4 --- /dev/null +++ b/tools/cmake/test_prog/prog_ns3.cpp @@ -0,0 +1,7 @@ +#include "ns3/simulator.h" + +int main() +{ + ns3::Simulator::GetNextEventTime(); + return 0; +} \ No newline at end of file -- 2.20.1