DScale
A fast, deterministic simulation framework for testing and benchmarking distributed systems. It simulates network latency, bandwidth constraints, and process execution in an event-driven environment with support for both single-threaded and parallel execution modes.
Usage
1. Install
In your project
cargo add dscale
2. Define Messages
Messages must implement the Message trait, which allows defining a virtual_size for bandwidth simulation.
use Message;
// Or (if there is no need in bandwidth)
3. Implement Process Logic
Implement ProcessHandle to define how your process reacts to initialization, messages, and timers.
use ;
use ;
use configuration;
;
4. Run the Simulation
Use SimulationBuilder to configure the topology, network constraints, and start the simulation.
use ;
Parallel Execution
For large simulations, enable parallel execution to distribute process steps across multiple threads:
let mut runner = default
.
.within_pool_latency
.time_budget
.parallel // use 8 worker threads
.build;
runner.run_full_budget;
When is parallel mode efficient?
- A lot of simulated processes (at least 200-300)
- on_message execution takes most of simulation time
- Independent work inside on_message (not so much synchronization)
Public API
Simulation Control
SimulationBuilder: Configures the simulation environment.default: Creates simulation with no processes and default parameters.seed: Sets the random seed for deterministic execution.time_budget: Sets the maximum simulation duration.add_pool: Creates a named pool of processes. (All processes also joinGLOBAL_POOL)within_pool_latency(pool, distribution): Configures latency between processes within a pool.between_pool_latency(pool_a, pool_b, distribution): Configures latency between two pools (symmetric). Every pool pair must have latency configured before callingbuild.vnic_bandwidth: Configures per-process network bandwidth limits for "virtual" NIC.Bounded(usize): Limits bandwidth (bytes per jiffy).Unbounded: No bandwidth limits (default).
simple: Selects single-threaded execution (default). Mutually exclusive withparallel— calling both panics.parallel(threads): Selects parallel execution with the given number of worker threads. Mutually exclusive withsimple— calling both panics.build: Finalizes configuration and returns a simulation runner.
run_full_budget: Runs the simulation until the time budget is exhausted.run_steps: Runs the simulation until it performs the requested number of steps or the global budget is exhausted.run_sub_budget: Runs the simulation until the sub-budget starting from current timepoint or global budget are exhausted.
Network Topology
GLOBAL_POOL:- Implicit pool containing all processes.
broadcastuses this pool.
- Implicit pool containing all processes.
Distributions:Uniform(low, high): Uniform distribution over[low, high].Bernoulli(p, value): With probabilitypthe latency isvalue, otherwise 0.Normal { mean, std_dev, low, high }: Truncated normal distribution clamped to[low, high].
Process Interaction (Context-Aware)
These functions are available globally but must be called within the context of a running process step.
broadcast: Shortcut forbroadcast_within_pool(GLOBAL_POOL).broadcast_within_pool: Sends a message to all processes within a named pool.send_to: Sends a message to a specific process by rank.send_random: Shortcut forsend_random_from_pool(GLOBAL_POOL).send_random_from_pool: Sends a message to a random process within a named pool.schedule_timer_after: Schedules a timer for the current process, returns aTimerId.rank: Returns the rank of the currently executing process. (Ranks start at 0)now: Returns the current simulation time.list_pool: Returns a slice of all process ranks in a pool.choose_from_pool: Picks a random process rank from a named pool.global_unique_id: Generates a globally unique monotonic ID.
Configuration (dscale::global::configuration)
seed: Returns the deterministic seed for the current process.process_number: Returns total number of processes in the simulation.
Key-Value Store (dscale::global::kv)
Thread-safe store for passing shared state, metrics, or configuration between processes or back to the host.
set(key, value): Stores a value under the given key.get(key) -> T: Retrieves a clone of the value (panics if missing or wrong type).modify(key, f): Mutates the value in place.
Macros
debug_process!: Logs a debug message prefixed with the current simulation time and process rank. Available at the crate root (use dscale::debug_process).
Helpers (dscale::helpers)
Combiner: Collects values until a threshold is reached, then yields them all at once. Useful for quorum-based logic.
Message Downcasting (MessagePtr)
try_as_type::<T>(): Attempts to downcast toT, returnsOption<&T>.as_type::<T>(): Downcasts toT, panics if the type does not match.is::<T>(): Returnstrueif the message is of typeT.
Logging Configuration (RUST_LOG)
DScale output is controlled via the RUST_LOG environment variable.
RUST_LOG=info: Shows high-level simulation status and a progress bar.RUST_LOG=debug: Enables alldebug_process!macro output and all internal simulation events.RUST_LOG=full::path::to::your::file::or::crate=debug,another::path=debug: Filter events only for your specific file or crate.
Note: RUST_LOG=debug and path-level debug filters only work without the --release flag.