Skip to content

Commit

Permalink
Added Criterion.rs and criterion.toml for cargo-criterion. Created `b…
Browse files Browse the repository at this point in the history
…enches` directory, `example_births_deaths`, `example_basic_infection` benchmarks, README.md explaining how to run benchmarks and profile with samply.
  • Loading branch information
RobertJacobsonCDC committed Jan 22, 2025
1 parent e723c6e commit b6a865b
Show file tree
Hide file tree
Showing 19 changed files with 295 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Criterion.rs output file.
profile.json

.vscode
.idea

Expand Down
42 changes: 25 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,30 @@ license = "Apache-2.0"
homepage = "https://github.com/CDCgov/ixa"

[dependencies]
fxhash = "^0.2.1"
rand = "^0.8.5"
csv = "^1.3.1"
serde = { version = "^1.0.217", features = ["derive"] }
serde_derive = "^1.0.217"
serde_json = "^1.0.135"
reikna = "^0.12.3"
fxhash = "0.2.1"
rand = "0.8.5"
csv = "1.3"
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0"
serde_json = "1.0.128"
reikna = "0.12.3"
roots = "0.0.8"
ixa-derive = { path = "ixa-derive" }
seq-macro = "^0.3.5"
paste = "^1.0.15"
ctor = "^0.2.8"
clap = { version = "^4.5.26", features = ["derive"] }
shlex = "^1.3.0"
rustyline = "^15.0.0"
seq-macro = "0.3.5"
paste = "1.0.15"
ctor = "0.2.8"
once_cell = "1.20.2"
clap = { version = "4.5.21", features = ["derive"] }
shlex = "1.3.0"
rustyline = "15.0.0"

[dev-dependencies]
rand_distr = "^0.4.3"
tempfile = "^3.15.0"
ordered-float = "^4.6.0"
assert_cmd = "^2.0.16"
rand_distr = "0.4.3"
tempfile = "3.3"
ordered-float = "4.3.0"
predicates = "3.1.2"
assert_cmd = "2.0.16"
criterion = "0.5.1"

[[bin]]
name = "runner_test_custom_args"
Expand All @@ -37,3 +40,8 @@ path = "tests/bin/runner_test_custom_args.rs"
[[bin]]
name = "runner_test_debug"
path = "tests/bin/runner_test_debug.rs"

[[bench]]
name = "example_births_deaths"
path = "benches/example_births_deaths/example_births_deaths.rs"
harness = false
113 changes: 113 additions & 0 deletions benches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Ixa Profiling and Benchmarking

For general Rust profiling information, see: https://nnethercote.github.io/perf-book/profiling.html.

# Generating Flamegraphs

You can use `samply` (Linux and macOS) to capture stack samples from your application and generate a flamegraph, helping you quickly identify and analyze performance hotspots.

## Prerequisites

Install `samply`:

```bash
cargo install samply
```

## Running

First build in release mode.

```bash
cargo build --example basic-infection --release
```

Then run the resulting binary with `samply`.

```bash
samply record -- target/release/examples/basic-infection
```

You can combine these two commands into one:

```bash
cargo build --example basic-infection --release && samply record -- target/release/examples/basic-infection
```

When it completes, `samply` will automatically open a browser with the generated report.


## `flamegraph` Alternative

You can use `flamegraph` if you prefer. It requires root privileges, but don't use `sudo cargo...`. Do this:

```bash
cargo flamegraph --root --example basic-infection
```

This will generate an SVG of the flamegraph in the current directory.

# Benchmarking Ixa

Ixa uses [Criterion.rs](https://bheisler.github.io/criterion.rs/book/index.html) for statistical benchmarking.

## Optional Prerequisites

- [`gnuplot`](http://www.gnuplot.info/): The [plotters crate](https://github.com/38/plotters) will be used as a fallback if `gnuplot` is not found.
- [cargo-criterion](https://bheisler.github.io/criterion.rs/book/cargo_criterion/cargo_criterion.html): This is the upcoming "next generation" of Criterion.rs. Eventually it will reduce compilation times and offer more features, but for now it only has feature parity.

```bash
cargo install cargo-criterion
```

## Running Benchmarks

### Using `cargo bench`

To run all benchmarks:

```bash
cargo bench
```

To run a specific benchmark called `example_births_deaths`:

```bash
cargo bench --bench example_births_deaths
```

To run a specific named benchmark group named `example_benches`:

```bash
cargo bench -- example_benches
```

### Using `cargo criterion`

To run all benchmarks:

```bash
cargo criterion
```

To run a specific benchmark file called `example_births_deaths`:

```bash
cargo criterion --bench example_births_deaths
```

To run only the benchmarks whose name or group matches `example_benches`:

```bash
cargo criterion -- example_benches
```

### Viewing Reports

An HTML report is created at `target/criterion/report/index.html`. On macOS:

```bash
open target/criterion/report/index.html
```

On Linux platforms, replace `open` with `xdg-open`, `gnome-open`, or `kde-open`, depending on your system configuration, or just open the file in a browser.
42 changes: 42 additions & 0 deletions benches/example_basic_infection/example_basic_infection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use criterion::{criterion_group, criterion_main, Criterion};
use ixa::context::Context;
use ixa::random::ContextRandomExt;

mod incidence_report;
mod infection_manager;
mod people;
mod transmission_manager;

static POPULATION: u64 = 1000;
static SEED: u64 = 123;
static MAX_TIME: f64 = 303.0;
static FOI: f64 = 0.1;
static INFECTION_DURATION: f64 = 5.0;

fn basic_infection() -> Context {
let mut context = Context::new();

context.init_random(SEED);

people::init(&mut context);
transmission_manager::init(&mut context);
infection_manager::init(&mut context);
incidence_report::init(&mut context).expect("failed to init incidence report");

context.add_plan(MAX_TIME, |context| {
context.shutdown();
});

context.execute();

context
}

pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("example basic-infection", |bencher| {
bencher.iter_with_large_drop(basic_infection)
});
}

criterion_group!(example_benches, criterion_benchmark);
criterion_main!(example_benches);
1 change: 1 addition & 0 deletions benches/example_basic_infection/incidence_report.rs
1 change: 1 addition & 0 deletions benches/example_basic_infection/infection_manager.rs
1 change: 1 addition & 0 deletions benches/example_basic_infection/people.rs
1 change: 1 addition & 0 deletions benches/example_basic_infection/transmission_manager.rs
1 change: 1 addition & 0 deletions benches/example_births_deaths/demographics_report.rs
49 changes: 49 additions & 0 deletions benches/example_births_deaths/example_births_deaths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::parameters_loader::Parameters;
use criterion::{criterion_group, criterion_main, Criterion};
use ixa::{Context, ContextGlobalPropertiesExt, ContextRandomExt};
use std::path::Path;

mod demographics_report;
mod incidence_report;
mod infection_manager;
mod parameters_loader;
mod population_manager;
mod transmission_manager;

fn births_deaths() -> Context {
let mut context = Context::new();
let current_dir = Path::new(file!()).parent().unwrap();
let file_path = current_dir.join("input.json");

parameters_loader::init_parameters(&mut context, &file_path)
.expect("failed to load parameters");

let parameters = context
.get_global_property_value(Parameters)
.unwrap()
.clone();
context.init_random(parameters.seed);

demographics_report::init(&mut context).expect("failed to init demographics report");
incidence_report::init(&mut context).expect("failed to init incidence report");

population_manager::init(&mut context);
transmission_manager::init(&mut context);
infection_manager::init(&mut context);

context.add_plan(parameters.max_time, |context| {
context.shutdown();
});

context.execute();
context
}

pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("example births-deaths", |bencher| {
bencher.iter_with_large_drop(births_deaths);
});
}

criterion_group!(example_benches, criterion_benchmark);
criterion_main!(example_benches);
1 change: 1 addition & 0 deletions benches/example_births_deaths/incidence_report.rs
1 change: 1 addition & 0 deletions benches/example_births_deaths/infection_manager.rs
1 change: 1 addition & 0 deletions benches/example_births_deaths/input.json
1 change: 1 addition & 0 deletions benches/example_births_deaths/parameters_loader.rs
1 change: 1 addition & 0 deletions benches/example_births_deaths/population_manager.rs
1 change: 1 addition & 0 deletions benches/example_births_deaths/transmission_manager.rs
48 changes: 48 additions & 0 deletions criterion.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# We use the defaults in every case. See:
# https://bheisler.github.io/criterion.rs/book/cargo_criterion/configuring_cargo_criterion.html.

# This is used to override the directory where cargo-criterion saves
# its data and generates reports.
#criterion_home = "./target/criterion"

# This is used to configure the format of cargo-criterion's command-line output.
# Options are:
# criterion: Prints confidence intervals for measurement and throughput, and
# indicates whether a change was detected from the previous run. The default.
# quiet: Like criterion, but does not indicate changes. Useful for simply
# presenting output numbers, eg. on a library's README.
# verbose: Like criterion, but prints additional statistics.
# bencher: Emulates the output format of the bencher crate and nightly-only
# libtest benchmarks.
#output_format = "criterion"

# This is used to configure the plotting backend used by cargo-criterion.
# Options are "gnuplot" and "plotters", or "auto", which will use gnuplot if it's
# available or plotters if it isn't.
ploting_backend = "auto"

# The colors table allows users to configure the colors used by the charts
# cargo-criterion generates.
[colors]
# These are used in many charts to compare the current measurement against
# the previous one.
#current_sample = {r = 31, g = 120, b = 180}
#previous_sample = {r = 7, g = 26, b = 28}

# These are used by the full PDF chart to highlight which samples were outliers.
#not_an_outlier = {r = 31, g = 120, b = 180}
#mild_outlier = {r = 5, g = 127, b = 0}
#severe_outlier = {r = 7, g = 26, b = 28}

# These are used for the line chart to compare multiple different functions.
#comparison_colors = [
# {r = 8, g = 34, b = 34},
# {r = 6, g = 139, b = 87},
# {r = 0, g = 139, b = 139},
# {r = 5, g = 215, b = 0},
# {r = 0, g = 0, b = 139},
# {r = 0, g = 20, b = 60},
# {r = 9, g = 0, b = 139},
# {r = 0, g = 255, b = 127},
#]

2 changes: 2 additions & 0 deletions examples/births-deaths/infection_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub fn init(context: &mut Context) {

#[cfg(test)]
mod test {
// Silence spurious unused import warnings.
#![allow(unused_imports)]
use super::*;
use ixa::context::Context;
use ixa::define_data_plugin;
Expand Down
2 changes: 2 additions & 0 deletions examples/births-deaths/population_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ pub fn init(context: &mut Context) {

#[cfg(test)]
mod test {
// Silence spurious unused import warnings.
#![allow(unused_imports)]
use super::*;
use crate::parameters_loader::{FoiAgeGroups, ParametersValues};
use ixa::context::Context;
Expand Down

0 comments on commit b6a865b

Please sign in to comment.