libcxxdes is a C++20 Discrete Event Simulation library inspired by SimPy. Just like Simpy, libcxxdes models discrete-event systems using processes and control-flow expressions. While its API feels familiar to SimPy users, being written in C++, it offers up to a few orders of magnitude performance improvement. Furthermore, discrete-event systems modeled using libcxxdes can be later integrated into other DES simulators whose kernels are written in C or C++, such as SystemC, gem5 or OMNET++.
What sets libcxxdes apart from these simulators is its use of modern C++ features to facilitate system modeling, most prominently the newly introduced coroutine support.
The following example demonstrates the modeling of an M/M/1 queueing system:
using namespace cxxdes::core;
using namespace cxxdes::core::time_ops;
CXXDES_SIMULATION(producer_consumer_example) {
/* ... */
coroutine<> producer() {
for (std::size_t i = 0; i < n_packets; ++i) {
// places an item to the queue
co_await q.put(now_seconds());
// models the arrival time (exponential random variable)
// env.timeout ensures that lambda() is in time units of the
// environment. Equivalent to lambda() * 1_s in this case.
co_await env.timeout(lambda());
}
}
coroutine<> consumer() {
std::size_t n = 0;
while (true) {
// blocks until an item is in the queue
auto x = co_await q.pop();
++n;
if (n == n_packets) {
// end of the experiment
// calculate the average latency
avg_latency = total_latency / n_packets;
co_return ;
}
// models the service time (exponential random variable)
co_await env.timeout(mu());
total_latency += (now_seconds() - x);
}
}
coroutine<> co_main() {
// start both producer() and consumer() in parallel
co_await (producer() && consumer());
}
};
libcxxdes tries to provide the complete feature set of SimPy, it currently supports/provides:
- Processes modeled using
coroutine<T>
s. - Control flow expressions:
- Parallel compositions:
co_await all_of(p1(), p2(), ...)
,co_await (p1() && p2)
,co_await any_of(p1(), p2(), ...)
,co_await (p1() || p2())
- Sequential compositions:
co_await sequential(p1(), p2(), ...)
,co_await (p1(), p2(), ...)
- Timeouts:
co_await delay(5)
,co_await timeout(5_s)
- Parallel compositions:
- Interruptable coroutines which are useful for modelling preemptive resources. --Not feasible to implement. Use || with a queue, same thing.
- Priority-scheduling of events that take place at the same simulation time.
coroutine<T>
can be assigned priorities! (lower the number, higher the priority) time_unit()
andtime_precision()
functions for mapping simulation time (integer) to real-world time.- Synchronization primitives, such as
mutex
,semaphore
,queue<T>
, andevent
. - RAII-style acquisition of resources using
co_with (resource) { /* */ }
syntax. - SimPy-compatible
resource
,container
andpreemptive_resource
. - Debugging facilities, such as getting the stack traces of
coroutine<T>
s. - A template-metaprogramming-based DSL to describe time accurately without suffering from the quirks of the floating-point aritmetic.
1_s + 500_ms + 100_us
is mapped to1'500'100
simulation if the precision is set to1_us
or to1'500
for a precision of1_ms
. - A CMake-based build system to facilitate integrating withn other projects.