diff --git a/Cargo.toml b/Cargo.toml index 3ffe8c1447f603cb07d6aa811f82ce931d79df8d..ec5f25f85512c03b1b1db74912c3aeafb769c6d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,10 @@ version = "0.1.0" authors = ["Dorian Weber <weber@informatik.hu-berlin.de>"] edition = "2018" +[features] +default = ["mock"] +mock = [] + [profile.release] lto = true opt-level = 3 diff --git a/build.rs b/build.rs index c1460dd0a70582b3033ec323205cb569b4325805..2131f3e40013173d535b39f5940dd66512fbbb05 100644 --- a/build.rs +++ b/build.rs @@ -105,5 +105,7 @@ fn main() { println!("cargo:warning=This library will still work, \ the C++ benchmarks will not!"); println!("cargo:warning={}", msg); + } else { + println!("cargo:rustc-cfg=feature=\"odemx\""); } } diff --git a/examples/barbershop.rs b/examples/barbershop.rs index 20e1d0399f07a153e541e30533797303253ca9d5..2b18c1ca92d370c3b3f2a18c088f55fc0deedab6 100644 --- a/examples/barbershop.rs +++ b/examples/barbershop.rs @@ -1,24 +1,24 @@ use simcore_rs::{Time, SimContext, Facility, RandomVar, Process}; -use rand::{rngs::SmallRng, SeedableRng, Rng}; +use rand::Rng; use std::cell::RefCell; +// helper constants +const SEED_A: u64 = 100000; +const SEED_S: u64 = 200000; + /// Globally shared data. -struct Shared { - rng_a: RefCell<SmallRng>, - rng_s: RefCell<SmallRng>, +struct Shared<R: Rng> { + rng_a: RefCell<R>, + rng_s: RefCell<R>, joe: Facility, wait_time: RandomVar } -// helper constants -const SEED_A: u64 = 100000; -const SEED_S: u64 = 200000; - /// Customer process with access to the barber and a random processing delay. struct Customer; impl Customer { - pub async fn actions(self, sim: SimContext<'_,Shared>) { + pub async fn actions(self, sim: SimContext<'_,Shared<impl Rng>>) { // access the barber and record the time for the report let arrival_time = sim.now(); sim.shared().joe.seize().await; @@ -33,7 +33,7 @@ impl Customer { } } -async fn barbershop(sim: SimContext<'_,Shared>, duration: Time) { +async fn barbershop<'b>(sim: SimContext<'b, Shared<impl Rng + 'b>>, duration: Time) { // activate a process to generate the customers sim.activate(async move { loop { @@ -54,6 +54,8 @@ async fn barbershop(sim: SimContext<'_,Shared>, duration: Time) { #[cfg(not(test))] fn main() { + use rand::{rngs::SmallRng, SeedableRng}; + let result = simcore_rs::simulation( // global data Shared { @@ -81,9 +83,9 @@ mod slx; mod bench { use super::*; use criterion::{Criterion, BenchmarkId, BatchSize, PlotConfiguration, - AxisScale, SamplingMode, criterion_group}; - use std::time::Duration; + AxisScale, criterion_group}; + #[cfg(feature = "odemx")] mod odemx { use std::os::raw::c_double; @@ -93,11 +95,13 @@ mod bench { } } #[cfg(windows)] - const SLX_PATH: &'static str = "C:/Wolverine/SLX/se64.exe"; + const SLX_PATH: &'static str = "C:\\Wolverine\\SLX"; const RANGE: u32 = 10; const STEP: Time = 1000.0; fn barbershop_bench(c: &mut Criterion) { + use rand::{rngs::SmallRng, SeedableRng}; + let mut group = c.benchmark_group("Barbershop"); // set-up the benchmark parameters @@ -106,16 +110,26 @@ mod bench { PlotConfiguration::default() .summary_scale(AxisScale::Logarithmic) ); - //group.sampling_mode(SamplingMode::Linear); - //group.measurement_time(Duration::from_secs(300)); + // group.sampling_mode(criterion::SamplingMode::Linear); + // group.measurement_time(std::time::Duration::from_secs(300)); - if !cfg!(windows) { - println!("Not running under Windows, skipping SLX benchmarks!"); - } + #[cfg(windows)] + let slx_path = { + let path = slx::slx_version(SLX_PATH); + + if path.is_none() { + println!("SLX not found, skipping SLX benchmarks!"); + } else { + println!("Using SLX program at {:?}", path.as_ref().unwrap()); + } + + path + }; // vary in the length of the simulation run for sim_duration in (0..RANGE).map(|c| Time::from(1 << c)*STEP) { - #[cfg(windows)] { + #[cfg(windows)] + if let Some(path) = slx_path.as_ref() { let duration = sim_duration.to_string(); let args = [ "/silent", @@ -133,7 +147,7 @@ mod bench { BenchmarkId::new("SLX", sim_duration), |b| b.iter_custom(|iters| slx::slx_bench( - SLX_PATH, + path.as_os_str(), &args, iters as usize ).expect("couldn't benchmark the SLX program") @@ -142,6 +156,7 @@ mod bench { } // benchmark the C++ implementation + #[cfg(feature = "odemx")] group.bench_function( BenchmarkId::new("ODEMx", sim_duration), |b| b.iter( diff --git a/examples/ferry.rs b/examples/ferry.rs index ac8dadecbab2dbf7b611c9b216678029a241329e..6c899954a1d08bb33c87cd8a53897427322d66c9 100644 --- a/examples/ferry.rs +++ b/examples/ferry.rs @@ -159,9 +159,9 @@ mod slx; mod bench { use super::*; use criterion::{Criterion, BenchmarkId, BatchSize, PlotConfiguration, - AxisScale, SamplingMode, criterion_group}; - use std::time::Duration; + AxisScale, criterion_group}; + #[cfg(feature = "odemx")] mod odemx { use std::os::raw::{c_double, c_uint}; @@ -171,7 +171,7 @@ mod bench { } } #[cfg(windows)] - const SLX_PATH: &'static str = "C:/Wolverine/SLX/se64.exe"; + const SLX_PATH: &'static str = "C:\\Wolverine\\SLX"; const RANGE: u32 = 10; const STEP: Time = 1000.0; @@ -184,16 +184,26 @@ mod bench { PlotConfiguration::default() .summary_scale(AxisScale::Logarithmic) ); - //group.sampling_mode(SamplingMode::Linear); - //group.measurement_time(Duration::from_secs(600)); + // group.sampling_mode(criterion::SamplingMode::Linear); + // group.measurement_time(std::time::Duration::from_secs(600)); - if !cfg!(windows) { - println!("Not running under Windows, skipping SLX benchmarks!"); - } + #[cfg(windows)] + let slx_path = { + let path = slx::slx_version(SLX_PATH); + + if path.is_none() { + println!("SLX not found, skipping SLX benchmarks!"); + } else { + println!("Using SLX program at {:?}", path.as_ref().unwrap()); + } + + path + }; // vary in the length of the simulation run for (i, sim_duration) in (0..RANGE).map(|c| Time::from(1 << c)*STEP).enumerate() { - #[cfg(windows)] { + #[cfg(windows)] + if let Some(path) = slx_path.as_ref() { let duration = sim_duration.to_string(); let args = [ "/silent", @@ -211,7 +221,7 @@ mod bench { BenchmarkId::new("SLX", sim_duration), |b| b.iter_custom(|iters| slx::slx_bench( - SLX_PATH, + path.as_os_str(), &args, iters as usize ).expect("couldn't benchmark the SLX program") @@ -220,6 +230,7 @@ mod bench { } // benchmark the C++ implementation + #[cfg(feature = "odemx")] group.bench_function( BenchmarkId::new("ODEMx", sim_duration), |b| b.iter(|| unsafe { diff --git a/examples/philosophers.rs b/examples/philosophers.rs index df1d7d347269cddd0c961dab92246b91dc5668db..e166a83161ba6370ae37861dbc68fc4cb0c3d870 100644 --- a/examples/philosophers.rs +++ b/examples/philosophers.rs @@ -154,9 +154,9 @@ mod slx; mod bench { use super::*; use criterion::{Criterion, BenchmarkId, PlotConfiguration, AxisScale, - SamplingMode, criterion_group}; - use std::time::Duration; + criterion_group}; + #[cfg(feature = "odemx")] mod odemx { use std::os::raw::c_uint; @@ -166,7 +166,7 @@ mod bench { } } #[cfg(windows)] - const SLX_PATH: &'static str = "C:/Wolverine/SLX/se64.exe"; + const SLX_PATH: &'static str = "C:\\Wolverine\\SLX"; const RANGE: u32 = 10; const STEP: usize = 3; @@ -179,16 +179,26 @@ mod bench { PlotConfiguration::default() .summary_scale(AxisScale::Logarithmic) ); - //group.sampling_mode(SamplingMode::Linear); - //group.measurement_time(Duration::from_secs(900)); + // group.sampling_mode(SamplingMode::Linear); + // group.measurement_time(std::time::Duration::from_secs(900)); - if !cfg!(windows) { - println!("Not running under Windows, skipping SLX benchmarks!"); - } + #[cfg(windows)] + let slx_path = { + let path = slx::slx_version(SLX_PATH); + + if path.is_none() { + println!("SLX not found, skipping SLX benchmarks!"); + } else { + println!("Using SLX program at {:?}", path.as_ref().unwrap()); + } + + path + }; // vary the number of performed reruns in each experiment for experiment_count in (0..RANGE).map(|c| (1 << c)*STEP) { - #[cfg(windows)] { + #[cfg(windows)] + if let Some(path) = slx_path.as_ref() { let count = experiment_count.to_string(); let args = [ "/silent", @@ -202,12 +212,11 @@ mod bench { ]; // benchmark the SLX implementation - group.sampling_mode(SamplingMode::Linear); group.bench_function( BenchmarkId::new("SLX", experiment_count), |b| b.iter_custom(|iters| slx::slx_bench( - SLX_PATH, + path.as_os_str(), &args, iters as usize ).expect("couldn't benchmark the SLX program") @@ -216,7 +225,7 @@ mod bench { } // benchmark the C++ implementation - group.sampling_mode(SamplingMode::Auto); + #[cfg(feature = "odemx")] group.bench_function( BenchmarkId::new("ODEMx", experiment_count), |b| b.iter(|| unsafe { @@ -228,7 +237,6 @@ mod bench { ); // benchmark the Rust implementation - group.sampling_mode(SamplingMode::Linear); group.bench_function( BenchmarkId::new("Rust", experiment_count), |b| b.iter(|| diff --git a/examples/slx/mod.rs b/examples/slx/mod.rs index f38c7cd1ad9006fe54c2a3f687f526c62dd67e05..089844acc2f21dc43004abc236999130c8acb0d7 100644 --- a/examples/slx/mod.rs +++ b/examples/slx/mod.rs @@ -1,11 +1,12 @@ -use std::{process::Command, time::Duration, iter::once}; +use std::{process::Command, time::Duration, path::Path, iter::once}; +use std::ffi::{OsString, OsStr}; use itertools::Itertools; /// Runs the SLX runtime environment once for an SLX program that measures its /// own duration and returns it on the standard output. /// /// Errors during this execution are returned back to the caller. -fn slx_run_once(program: &str, arguments: &[&str], iterations: usize) -> Result<Duration, (i32, String)> { +fn slx_run_once(program: &OsStr, arguments: &[&str], iterations: usize) -> Result<Duration, (i32, String)> { // start the SLX runtime and wait for its return let rc = Command::new(program) .args(arguments) @@ -45,7 +46,7 @@ fn slx_run_once(program: &str, arguments: &[&str], iterations: usize) -> Result< /// /// The SLX program in question has to report its own real-time duration using /// the standard output. -pub fn slx_bench(program: &str, arguments: &[&str], iterations: usize) -> Result<Duration, String> { +pub fn slx_bench(program: &OsStr, arguments: &[&str], iterations: usize) -> Result<Duration, String> { // try to complete the iterations in a single run of the SLX program slx_run_once(program, arguments, iterations) .or_else(|(code, desc)| { @@ -77,3 +78,25 @@ pub fn slx_bench(program: &str, arguments: &[&str], iterations: usize) -> Result } }) } + +/// Attempts to find the best SLX version for the benchmarks, falling back on +/// the free version if no SLX license can be found. +pub fn slx_version(base: impl AsRef<Path>) -> Option<OsString> { + if base.as_ref().exists() { + let programs = ["se64.exe", "se32.exe", "sse.exe"]; + let args = ["/silent", "/noicon", "/nowarn", "/noxwarn", "bad_file.slx"]; + + for path in programs.iter().map(|p| base.as_ref().join(p)) { + match Command::new(&path).args(&args).status() { + Err(_) => continue, + Ok(rc) => if let Some(code) = rc.code() { + if code == -10003 { + return Some(path.into_os_string()) + } + } + } + } + } + + None +}