Skip to content

Commit

Permalink
MultiFizzBuzz Ranges (#45)
Browse files Browse the repository at this point in the history
Enable `MultiFizzBuzz` on `Range<i32>`, `RangeInclusive<i16>` and anything else which rayon can convert to an `IndexedParallelIterator`
  • Loading branch information
MusicalNinjaDad committed Jun 1, 2024
1 parent 82cb35f commit a693fe2
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 9 deletions.
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"extensions": [
// rust
"rust-lang.rust-analyzer",
"vadimcn.vscode-lldb",
// python
"ms-python.python",
"ms-python.vscode-pylance",
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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<i32>` (but not `RangeInclusive<i32>` 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)
Expand Down
7 changes: 6 additions & 1 deletion rust/fizzbuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fizzbuzz"
version = "2.1.0"
version = "3.0.0"
edition = "2021"

[lib]
Expand All @@ -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
18 changes: 18 additions & 0 deletions rust/fizzbuzz/benches/bench_fizzbuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand All @@ -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);
Expand Down
41 changes: 41 additions & 0 deletions rust/fizzbuzz/benches/bench_sizes.rs
Original file line number Diff line number Diff line change
@@ -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);
35 changes: 27 additions & 8 deletions rust/fizzbuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,26 @@ where
/// ### Required:
/// - fn fizzbuzz(self) -> FizzBuzzAnswer
pub trait MultiFizzBuzz {
fn fizzbuzz(&self) -> FizzBuzzAnswer;
fn fizzbuzz(self) -> FizzBuzzAnswer;
}

impl<Num> MultiFizzBuzz for Vec<Num>
impl<Iterable, Num> MultiFizzBuzz for Iterable
where
Num: FizzBuzz + Sync,
Iterable: rayon::iter::IntoParallelIterator<Item = Num>,
<Iterable as 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())
}
}
}
Expand All @@ -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<String> = 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<String> = vec![];
for i in 1..20 {
expected.push(i.fizzbuzz().into())
}
let output: Vec<String> = input.fizzbuzz().into();
assert_eq!(output, expected)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,20 @@ mod vectors {
assert_eq!(answer, expected)
}
}

mod ranges {
use super::*;

#[test]
fn test_small_range() {
let answer: Vec<String> = (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)
}
}

0 comments on commit a693fe2

Please sign in to comment.