Skip to content

Benchmarking using Criterion #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
578 changes: 578 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -9,3 +9,10 @@ publish = false

[dependencies]
pico-args = "0.5.0"

[dev-dependencies]
criterion = "0.4"

[[bench]]
name = "benchmark"
harness = false
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ _Total timing_ is computed from individual solution _timings_ and excludes as mu
cargo test
```

To run tests for a specific day, append `--bin <day>`, e.g. `cargo test --bin 01`. You can further scope it down to a specific part, e.g. `cargo test --bin 01 part_one`.
To run tests for a specific day, append `--lib <day>`, e.g. `cargo test --lib 01`. You can further scope it down to a specific part, e.g. `cargo test --lib 01 part_one`.

### Format code

27 changes: 27 additions & 0 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use advent_of_code::day01::{part_one, part_two};
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn test<F>(c: &mut Criterion, day: u8, part: u8, mut f: F) -> &mut Criterion
where
F: FnMut(&str) -> Option<u32>,
{
let id = format!("Day {} Part {}", day, part);

let input = advent_of_code::read_file("examples", day);

if f(&input) == None {
return c;
}

c.bench_function(&id, |b| b.iter(|| f(black_box(&input.to_string()))));

return c;
}

pub fn criterion_benchmark(c: &mut Criterion) {
test(c, 1, 1, part_one);
test(c, 1, 2, part_two);
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
7 changes: 7 additions & 0 deletions src/bin/01.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use advent_of_code::day01::{part_one, part_two};

fn main() {
let input = &advent_of_code::read_file("inputs", 1);
advent_of_code::solve!(1, part_one, input);
advent_of_code::solve!(2, part_two, input);
}
125 changes: 108 additions & 17 deletions src/bin/scaffold.rs
Original file line number Diff line number Diff line change
@@ -4,42 +4,45 @@
*/
use std::{
fs::{File, OpenOptions},
io::Write,
process,
io::{Error, Read, Seek, Write},
process::{self, Command},
};

const MODULE_TEMPLATE: &str = r###"pub fn part_one(input: &str) -> Option<u32> {
const MODULE_LIB_TEMPLATE: &str = r###"pub fn part_one(input: &str) -> Option<u32> {
None
}

pub fn part_two(input: &str) -> Option<u32> {
None
}

fn main() {
let input = &advent_of_code::read_file("inputs", DAY);
advent_of_code::solve!(1, part_one, input);
advent_of_code::solve!(2, part_two, input);
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one() {
let input = advent_of_code::read_file("examples", DAY);
let input = crate::read_file("examples", DAY);
assert_eq!(part_one(&input), None);
}

#[test]
fn test_part_two() {
let input = advent_of_code::read_file("examples", DAY);
let input = crate::read_file("examples", DAY);
assert_eq!(part_two(&input), None);
}
}
"###;

const MODULE_BIN_TEMPLATE: &str = r###"use advent_of_code::dayDAYPAD::{part_one, part_two};

fn main() {
let input = &advent_of_code::read_file("inputs", DAY);
advent_of_code::solve!(1, part_one, input);
advent_of_code::solve!(2, part_two, input);
}
"###;

fn parse_args() -> Result<u8, pico_args::Error> {
let mut args = pico_args::Arguments::from_env();
args.free_from_str()
@@ -53,6 +56,46 @@ fn create_file(path: &str) -> Result<File, std::io::Error> {
OpenOptions::new().write(true).create(true).open(path)
}

fn modify_lib(day: u8) -> Result<(), Error> {
let lib_path = "src/lib.rs";

let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(lib_path)?;

let mut old_code = String::new();
file.read_to_string(&mut old_code)?;

file.rewind()?;
file.set_len(0)?;

match file.write_all(
old_code
.replace(
&format!("// pub mod day{:02};", day),
&format!("pub mod day{:02};", day),
)
.as_bytes(),
) {
Ok(_) => {
println!("Updated lib.rs to include 'day{:02}' module", day);
}
Err(e) => {
eprintln!(
"Failed to write updateded lib.rs to include 'day{:02}' module: {}",
day, e
);
process::exit(1);
}
}

file.flush()?;

Ok(())
}

fn main() {
let day = match parse_args() {
Ok(day) => day,
@@ -66,22 +109,59 @@ fn main() {

let input_path = format!("src/inputs/{}.txt", day_padded);
let example_path = format!("src/examples/{}.txt", day_padded);
let module_path = format!("src/bin/{}.rs", day_padded);
let module_bin_path = format!("src/bin/{}.rs", day_padded);
let module_lib_path = format!("src/day{}.rs", day_padded);

match modify_lib(day) {
Ok(_) => {}
Err(e) => {
eprintln!("Failed to update lib.rs file: {}", e);
process::exit(1);
}
}

let mut bin_file = match safe_create_file(&module_bin_path) {
Ok(file) => file,
Err(e) => {
eprintln!("Failed to create module bin file: {}", e);
process::exit(1);
}
};

let mut file = match safe_create_file(&module_path) {
match bin_file.write_all(
MODULE_BIN_TEMPLATE
.replace("DAYPAD", &day_padded)
.replace("DAY", &day.to_string())
.as_bytes(),
) {
Ok(_) => {
println!("Created module bin file \"{}\"", &module_bin_path);
}
Err(e) => {
eprintln!("Failed to write bin module contents: {}", e);
process::exit(1);
}
}

let mut lib_file = match safe_create_file(&module_lib_path) {
Ok(file) => file,
Err(e) => {
eprintln!("Failed to create module file: {}", e);
eprintln!("Failed to create module lib file: {}", e);
process::exit(1);
}
};

match file.write_all(MODULE_TEMPLATE.replace("DAY", &day.to_string()).as_bytes()) {
match lib_file.write_all(
MODULE_LIB_TEMPLATE
.replace("DAYPAD", &day_padded)
.replace("DAY", &day.to_string())
.as_bytes(),
) {
Ok(_) => {
println!("Created module file \"{}\"", &module_path);
println!("Created module lib file \"{}\"", &module_bin_path);
}
Err(e) => {
eprintln!("Failed to write module contents: {}", e);
eprintln!("Failed to write lib module contents: {}", e);
process::exit(1);
}
}
@@ -106,6 +186,17 @@ fn main() {
}
}

// We need to execute cargo clean -p advent_of_code otherwise there can be cache issues when executing cargo check
// As far as I can tell this relates to https://github.com/rust-lang/cargo/issues/6529
let clean_output = Command::new("cargo")
.args(["clean", "-p", "advent_of_code"])
.output()
.unwrap();

if !clean_output.status.success() {
eprint!("Failed to execute 'cargo clean -p advent_of_code'.");
}

println!("---");
println!(
"🎄 Type `cargo solve {}` to run your solution.",
81 changes: 81 additions & 0 deletions src/day01.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
pub fn lines_as_numbers(input: &str) -> impl Iterator<Item = Option<u32>> + '_ {
let result = input
.split('\n')
.map(|it| -> Option<u32> { it.parse::<u32>().ok() });

result
}

pub fn part_one(input: &str) -> Option<u32> {
let lines = lines_as_numbers(input);

let mut max = 0;
let mut current = 0;

for line in lines {
match line {
Some(calories) => {
current += calories;
}
None => {
if current > max {
max = current;
}
current = 0;
}
}
}

Some(max)
}

pub fn part_two(input: &str) -> Option<u32> {
let mut lines: Vec<_> = lines_as_numbers(input).collect();
lines.push(None);

let mut max1 = 0;
let mut max2 = 0;
let mut max3 = 0;

let mut current = 0;

for line in lines {
match line {
Some(calories) => {
current += calories;
}
None => {
if current > max1 {
max3 = max2;
max2 = max1;
max1 = current;
} else if current > max2 {
max3 = max2;
max2 = current;
} else if current > max3 {
max3 = current;
}
current = 0;
}
}
}

Some(max1 + max2 + max3)
}

#[cfg(test)]
pub mod tests {
use super::*;

#[test]
fn test_part_one() {
let input = crate::read_file("examples", 1);
assert_eq!(part_one(&input), Some(24000));
}

#[test]
fn test_part_two() {
let input = crate::read_file("examples", 1);
assert_eq!(part_two(&input), Some(45000));
}
}
14 changes: 14 additions & 0 deletions src/examples/01.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
28 changes: 28 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -6,6 +6,34 @@
use std::env;
use std::fs;

// modules for the different days are automatically uncomented by the scaffold command
pub mod day01;
// pub mod day02;
// pub mod day01;
// pub mod day02;
// pub mod day03;
// pub mod day04;
// pub mod day05;
// pub mod day06;
// pub mod day07;
// pub mod day08;
// pub mod day09;
// pub mod day10;
// pub mod day11;
// pub mod day12;
// pub mod day13;
// pub mod day14;
// pub mod day15;
// pub mod day16;
// pub mod day17;
// pub mod day18;
// pub mod day19;
// pub mod day20;
// pub mod day21;
// pub mod day22;
// pub mod day23;
// pub mod day24;
// pub mod day25;
pub mod helpers;

pub const ANSI_ITALIC: &str = "\x1b[3m";