diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9a1ef2a..27e2950 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,6 +5,7 @@ "extensions": [ // rust "rust-lang.rust-analyzer", + "vadimcn.vscode-lldb", // python "ms-python.python", "ms-python.vscode-pylance", diff --git a/CHANGELOG.md b/CHANGELOG.md index c764a99..b458e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # FizzBuzz Changelog +## Rust 3.0.0 + +- **BREAKING CHANGE**: MultiFizzBuzz will consume the input. +- MultiFizzBuzz can accept any type of input which will provide a `rayon::iter::IndexedParallelIterator` via `rayon::iter::IntoParallelIterator`. Specifically this is tested to also accept `Range` (but not `RangeInclusive` or Ranges of larger types). + ## Python 2.0.1 - Update pyo3testing framework used for rust exports to v0.3.4 (Simplifies unit tests in rust source) diff --git a/rust/fizzbuzz/Cargo.toml b/rust/fizzbuzz/Cargo.toml index d108e2e..743a6cf 100644 --- a/rust/fizzbuzz/Cargo.toml +++ b/rust/fizzbuzz/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fizzbuzz" -version = "2.1.0" +version = "3.0.0" edition = "2021" [lib] @@ -19,4 +19,9 @@ criterion = { version = "0.5", features = ["html_reports"] } name = "bench_fizzbuzz" harness = false +[[bench]] +name = "bench_sizes" +harness = false + + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rust/fizzbuzz/benches/bench_fizzbuzz.rs b/rust/fizzbuzz/benches/bench_fizzbuzz.rs index 03cd252..dc15907 100644 --- a/rust/fizzbuzz/benches/bench_fizzbuzz.rs +++ b/rust/fizzbuzz/benches/bench_fizzbuzz.rs @@ -54,6 +54,18 @@ fn multifizzbuzz_trait_as_string() { let _: String = inputs.fizzbuzz().into(); } +#[inline] +fn multifizzbuzz_trait_from_vec_as_answer() { + let inputs: Vec<_> = (1..TEST_SIZE).collect(); + let _ = inputs.fizzbuzz(); +} + +#[inline] +fn multifizzbuzz_trait_from_range_as_answer() { + let inputs = 1..TEST_SIZE; + let _ = inputs.fizzbuzz(); +} + fn criterion_benchmark(c: &mut Criterion) { // c.bench_function("for_loop", |b| b.iter(|| for_loop())); // c.bench_function("for_loop_with_vec_overhead", |b| { @@ -66,6 +78,12 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("multifizzbuzz_trait_as_string", |b| { b.iter(|| multifizzbuzz_trait_as_string()) }); + c.bench_function("multifizzbuzz_trait_from_vec_as_answer", |b| { + b.iter(|| multifizzbuzz_trait_from_vec_as_answer()) + }); + c.bench_function("multifizzbuzz_trait_from_range_as_answer", |b| { + b.iter(|| multifizzbuzz_trait_from_range_as_answer()) + }); } criterion_group!(benches, criterion_benchmark); diff --git a/rust/fizzbuzz/benches/bench_sizes.rs b/rust/fizzbuzz/benches/bench_sizes.rs new file mode 100644 index 0000000..0273923 --- /dev/null +++ b/rust/fizzbuzz/benches/bench_sizes.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] +use criterion::{criterion_group, criterion_main, Criterion}; +use fizzbuzz::{self, MultiFizzBuzz}; + +#[inline] +fn range_20() { + const TEST_SIZE: i32 = 20; + let _ = (1..TEST_SIZE).fizzbuzz(); +} + +#[inline] +fn range_200_000() { + const TEST_SIZE: i32 = 200_000; + let _ = (1..TEST_SIZE).fizzbuzz(); +} +#[inline] +fn range_300_000() { + const TEST_SIZE: i32 = 300_000; + let _ = (1..TEST_SIZE).fizzbuzz(); +} +#[inline] +fn range_1_000_000() { + const TEST_SIZE: i32 = 1_000_000; + let _ = (1..TEST_SIZE).fizzbuzz(); +} +#[inline] +fn range_10_000_000() { + const TEST_SIZE: i32 = 10_000_000; + let _ = (1..TEST_SIZE).fizzbuzz(); +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("20", |b| b.iter(|| range_20())); + c.bench_function("200_000", |b| b.iter(|| range_200_000())); + c.bench_function("300_000", |b| b.iter(|| range_300_000())); + c.bench_function("1_000_000", |b| b.iter(|| range_1_000_000())); + c.bench_function("10_000_000", |b| b.iter(|| range_10_000_000())); +} + +criterion_group!(sizes, criterion_benchmark); +criterion_main!(sizes); diff --git a/rust/fizzbuzz/src/lib.rs b/rust/fizzbuzz/src/lib.rs index d122a92..1fa99e4 100644 --- a/rust/fizzbuzz/src/lib.rs +++ b/rust/fizzbuzz/src/lib.rs @@ -100,18 +100,26 @@ where /// ### Required: /// - fn fizzbuzz(self) -> FizzBuzzAnswer pub trait MultiFizzBuzz { - fn fizzbuzz(&self) -> FizzBuzzAnswer; + fn fizzbuzz(self) -> FizzBuzzAnswer; } -impl MultiFizzBuzz for Vec +impl MultiFizzBuzz for Iterable where - Num: FizzBuzz + Sync, + Iterable: rayon::iter::IntoParallelIterator, + ::Iter: IndexedParallelIterator, + Num: FizzBuzz, { - fn fizzbuzz(&self) -> FizzBuzzAnswer { - if self.len() < BIG_VECTOR { - FizzBuzzAnswer::Many(self.iter().map(|n| n.fizzbuzz().into()).collect()) + fn fizzbuzz(self) -> FizzBuzzAnswer { + let par_iter = self.into_par_iter(); + if par_iter.len() < BIG_VECTOR { + FizzBuzzAnswer::Many( + par_iter + .with_min_len(BIG_VECTOR) //Don't parallelise when small + .map(|n| n.fizzbuzz().into()) + .collect(), + ) } else { - FizzBuzzAnswer::Many(self.par_iter().map(|n| n.fizzbuzz().into()).collect()) + FizzBuzzAnswer::Many(par_iter.map(|n| n.fizzbuzz().into()).collect()) } } } @@ -137,11 +145,22 @@ mod test { #[test] fn big_vector_is_well_ordered() { let input: Vec<_> = (1..BIG_VECTOR + 1).collect(); - let output: Vec<_> = input.fizzbuzz().into(); + let output: Vec<_> = input.clone().fizzbuzz().into(); let mut expected: Vec = vec![]; for i in input.iter() { expected.push(i.fizzbuzz().into()) } assert_eq!(output, expected); } + + #[test] + fn fizzbuzz_range() { + let input = 1..20; + let mut expected: Vec = vec![]; + for i in 1..20 { + expected.push(i.fizzbuzz().into()) + } + let output: Vec = input.fizzbuzz().into(); + assert_eq!(output, expected) + } } diff --git a/rust/fizzbuzz/tests/test_vec.rs b/rust/fizzbuzz/tests/test_multifizzbuzz.rs similarity index 53% rename from rust/fizzbuzz/tests/test_vec.rs rename to rust/fizzbuzz/tests/test_multifizzbuzz.rs index 89d8740..f9bb1d7 100644 --- a/rust/fizzbuzz/tests/test_vec.rs +++ b/rust/fizzbuzz/tests/test_multifizzbuzz.rs @@ -18,3 +18,20 @@ mod vectors { assert_eq!(answer, expected) } } + +mod ranges { + use super::*; + + #[test] + fn test_small_range() { + let answer: Vec = (1..=5_i16).fizzbuzz().into(); + let expected = vec![ + "1".to_string(), + "2".to_string(), + "fizz".to_string(), + "4".to_string(), + "buzz".to_string(), + ]; + assert_eq!(answer, expected) + } +}