From 8f85294a9c2876428da4c0b30ec38fa7b22c9675 Mon Sep 17 00:00:00 2001 From: MusicalNinjaDad <102677655+MusicalNinjaDad@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:01:23 +0200 Subject: [PATCH] Performance improvements - new return types in rust (#57) ## Rust 4.0.0 & Python 3.0.1 - **BREAKING CHANGES - Rust**: new return types for significant performance improvements: - `MultiFizzBuzz` now lazily returns a rayon IndexedParallelIterator - `FizzBuzz` returns a `FizzBuzzAnswer` which can be converted into a `String` or `Cow` - `FizzBuzzAnswer` now represents the valid answers to FizzBuzz, not `One(String)`or `Many(Vec>String>)` - Python implementation updated to work with rust v4.0.0 which brings slight performance improvements (approx 10-20%) ## Summary by Sourcery This pull request introduces significant performance improvements by updating the return types for `FizzBuzz` and `MultiFizzBuzz`. The `FizzBuzzAnswer` enum has been refactored to directly represent valid FizzBuzz answers. The Python implementation has been updated to work with Rust v4.0.0, bringing slight performance improvements. Documentation and tests have been updated accordingly. * **New Features**: - Introduced new return types for `FizzBuzz` and `MultiFizzBuzz` to improve performance, including lazy evaluation with `rayon::iter::IndexedParallelIterator`. * **Enhancements**: - Updated `FizzBuzzAnswer` to represent valid FizzBuzz answers directly, removing the need for `One(String)` and `Many(Vec)` variants. - Enhanced the Python implementation to work with Rust v4.0.0, resulting in slight performance improvements (approx 10-20%). * **Documentation**: - Updated documentation to reflect new return types and usage examples for `FizzBuzz` and `MultiFizzBuzz`. * **Tests**: - Added new tests to cover the updated `FizzBuzzAnswer` and `MultiFizzBuzz` implementations, including tests for negative numbers and non-whole numbers. - Updated existing tests to use the new return types and ensure compatibility with the new implementation. --- CHANGELOG.md | 8 + __pyversion__ | 2 +- rust/fizzbuzz/Cargo.toml | 2 +- rust/fizzbuzz/benches/bench_fizzbuzz.rs | 24 +-- rust/fizzbuzz/benches/bench_sizes.rs | 13 +- rust/fizzbuzz/src/lib.rs | 138 ++++++++++-------- .../{test_numbertypes.rs => test_fizzbuzz.rs} | 80 ++++++++-- rust/fizzbuzz/tests/test_multifizzbuzz.rs | 15 +- rust/fizzbuzzo3/src/lib.rs | 47 +++--- tests/perf_results.md | 30 ++++ 10 files changed, 243 insertions(+), 116 deletions(-) rename rust/fizzbuzz/tests/{test_numbertypes.rs => test_fizzbuzz.rs} (60%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c35ec54..7d86b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # FizzBuzz Changelog +## Rust 4.0.0 & Python 3.0.1 + +- **BREAKING CHANGES - Rust**: new return types for significant performance improvements: + - `MultiFizzBuzz` now lazily returns a rayon IndexedParallelIterator + - `FizzBuzz` returns a `FizzBuzzAnswer` which can be converted into a `String` or `Cow` + - `FizzBuzzAnswer` now represents the valid answers to FizzBuzz, not `One(String)`or `Many(Vec>String>)` +- Python implementation updated to work with rust v4.0.0 which brings slight performance improvements (approx 10-20%) + ## Python 3.0.0 - Return `list[str]` when passed a `list` or `slice`, continue to return `str`when passed a single `int`. diff --git a/__pyversion__ b/__pyversion__ index 56fea8a..13d683c 100644 --- a/__pyversion__ +++ b/__pyversion__ @@ -1 +1 @@ -3.0.0 \ No newline at end of file +3.0.1 \ No newline at end of file diff --git a/rust/fizzbuzz/Cargo.toml b/rust/fizzbuzz/Cargo.toml index 6cc7527..2352976 100644 --- a/rust/fizzbuzz/Cargo.toml +++ b/rust/fizzbuzz/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fizzbuzz" -version = "3.0.2" +version = "4.0.0" edition = "2021" [lib] diff --git a/rust/fizzbuzz/benches/bench_fizzbuzz.rs b/rust/fizzbuzz/benches/bench_fizzbuzz.rs index dc15907..ecd6b73 100644 --- a/rust/fizzbuzz/benches/bench_fizzbuzz.rs +++ b/rust/fizzbuzz/benches/bench_fizzbuzz.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] +use std::borrow::Cow; + use criterion::{criterion_group, criterion_main, Criterion}; -use fizzbuzz::{self, FizzBuzz, MultiFizzBuzz}; +use fizzbuzz::{self, FizzBuzz, FizzBuzzAnswer, MultiFizzBuzz}; use rayon::prelude::*; static TEST_SIZE: isize = 1_000_000; @@ -43,27 +45,27 @@ fn vec_pariter() { } #[inline] -fn multifizzbuzz_trait() { +fn multifizzbuzz_trait_as_vec_string() { let inputs: Vec<_> = (1..TEST_SIZE).collect(); - let _: Vec = inputs.fizzbuzz().into(); + let _: Vec = inputs.fizzbuzz().collect(); } #[inline] -fn multifizzbuzz_trait_as_string() { +fn multifizzbuzz_trait_as_vec_cow() { let inputs: Vec<_> = (1..TEST_SIZE).collect(); - let _: String = inputs.fizzbuzz().into(); + let _: Vec> = inputs.fizzbuzz().collect(); } #[inline] fn multifizzbuzz_trait_from_vec_as_answer() { let inputs: Vec<_> = (1..TEST_SIZE).collect(); - let _ = inputs.fizzbuzz(); + let _: Vec = inputs.fizzbuzz().collect(); } #[inline] fn multifizzbuzz_trait_from_range_as_answer() { let inputs = 1..TEST_SIZE; - let _ = inputs.fizzbuzz(); + let _: Vec = inputs.fizzbuzz().collect(); } fn criterion_benchmark(c: &mut Criterion) { @@ -74,9 +76,11 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("vec_iter", |b| b.iter(|| vec_iter())); // c.bench_function("vec_intoiter", |b| b.iter(|| vec_intoiter())); c.bench_function("vec_pariter", |b| b.iter(|| vec_pariter())); - c.bench_function("multifizzbuzz_trait", |b| b.iter(|| multifizzbuzz_trait())); - c.bench_function("multifizzbuzz_trait_as_string", |b| { - b.iter(|| multifizzbuzz_trait_as_string()) + c.bench_function("multifizzbuzz_trait_as_vec_string", |b| { + b.iter(|| multifizzbuzz_trait_as_vec_string()) + }); + c.bench_function("multifizzbuzz_trait_as_vec_cow", |b| { + b.iter(|| multifizzbuzz_trait_as_vec_cow()) }); c.bench_function("multifizzbuzz_trait_from_vec_as_answer", |b| { b.iter(|| multifizzbuzz_trait_from_vec_as_answer()) diff --git a/rust/fizzbuzz/benches/bench_sizes.rs b/rust/fizzbuzz/benches/bench_sizes.rs index 0273923..8c6143d 100644 --- a/rust/fizzbuzz/benches/bench_sizes.rs +++ b/rust/fizzbuzz/benches/bench_sizes.rs @@ -1,32 +1,33 @@ #![allow(dead_code)] use criterion::{criterion_group, criterion_main, Criterion}; -use fizzbuzz::{self, MultiFizzBuzz}; +use fizzbuzz::{self, FizzBuzzAnswer, MultiFizzBuzz}; +use rayon::iter::ParallelIterator; #[inline] fn range_20() { const TEST_SIZE: i32 = 20; - let _ = (1..TEST_SIZE).fizzbuzz(); + let _: Vec = (1..TEST_SIZE).fizzbuzz().collect(); } #[inline] fn range_200_000() { const TEST_SIZE: i32 = 200_000; - let _ = (1..TEST_SIZE).fizzbuzz(); + let _: Vec = (1..TEST_SIZE).fizzbuzz().collect(); } #[inline] fn range_300_000() { const TEST_SIZE: i32 = 300_000; - let _ = (1..TEST_SIZE).fizzbuzz(); + let _: Vec = (1..TEST_SIZE).fizzbuzz().collect(); } #[inline] fn range_1_000_000() { const TEST_SIZE: i32 = 1_000_000; - let _ = (1..TEST_SIZE).fizzbuzz(); + let _: Vec = (1..TEST_SIZE).fizzbuzz().collect(); } #[inline] fn range_10_000_000() { const TEST_SIZE: i32 = 10_000_000; - let _ = (1..TEST_SIZE).fizzbuzz(); + let _: Vec = (1..TEST_SIZE).fizzbuzz().collect(); } fn criterion_benchmark(c: &mut Criterion) { diff --git a/rust/fizzbuzz/src/lib.rs b/rust/fizzbuzz/src/lib.rs index 896988b..5967a34 100644 --- a/rust/fizzbuzz/src/lib.rs +++ b/rust/fizzbuzz/src/lib.rs @@ -2,44 +2,61 @@ //! constructed from `0`,`3`,`5` and support the `%` operator. //! //! -//! ## Example usage: +//! ## Example usage for single item: //! //! ``` //! use fizzbuzz::FizzBuzz; +//! use std::borrow::Cow; //! -//! let one: String = 1.fizzbuzz().into(); +//! let one: Cow = 1.fizzbuzz().into(); //! let three: String = 3.fizzbuzz().into(); -//! assert_eq!(one, "1".to_string()); -//! assert_eq!(three, "fizz".to_string()); +//! assert_eq!(&one, "1"); +//! assert_eq!(three, "fizz"); +//! ``` +//! +//! ## Example usage for multiple items: +//! +//! ``` +//! use fizzbuzz::MultiFizzBuzz; +//! use rayon::iter::ParallelIterator; // required to `.collect()` the results +//! +//! let one_to_five = vec![1,2,3,4,5]; +//! let fizzbuzzed: Vec = one_to_five.fizzbuzz().collect(); +//! assert_eq!(fizzbuzzed, vec!["1", "2", "fizz", "4", "buzz"]); //! ``` +use std::borrow::Cow; + use rayon::prelude::*; static BIG_VECTOR: usize = 300_000; // Size from which parallelisation makes sense #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -/// Provides conversion to `String` and `Vec` via `.into()`, -/// ::From() etc. +/// Represents a valid answer to fizzbuzz and provides conversion to `String` and `Cow<&str>` via `.into()` pub enum FizzBuzzAnswer { - /// Stores a single FizzBuzz value - One(String), - /// Stores a series of FizzBuzz values - Many(Vec), + Fizz, + Buzz, + Fizzbuzz, + Number(String), } -impl From for String { - fn from(value: FizzBuzzAnswer) -> Self { - match value { - FizzBuzzAnswer::One(s) => s, - FizzBuzzAnswer::Many(v) => v.join(", "), +impl From for Cow<'static, str> { + fn from(answer: FizzBuzzAnswer) -> Self { + match answer { + FizzBuzzAnswer::Fizz => "fizz".into(), + FizzBuzzAnswer::Buzz => "buzz".into(), + FizzBuzzAnswer::Fizzbuzz => "fizzbuzz".into(), + FizzBuzzAnswer::Number(n) => n.into(), } } } -impl From for Vec { - fn from(value: FizzBuzzAnswer) -> Self { - match value { - FizzBuzzAnswer::One(s) => vec![s], - FizzBuzzAnswer::Many(v) => v, +impl From for String { + fn from(answer: FizzBuzzAnswer) -> Self { + match answer { + FizzBuzzAnswer::Fizz => "fizz".into(), + FizzBuzzAnswer::Buzz => "buzz".into(), + FizzBuzzAnswer::Fizzbuzz => "fizzbuzz".into(), + FizzBuzzAnswer::Number(n) => n, } } } @@ -76,79 +93,78 @@ where fn fizzbuzz(&self) -> FizzBuzzAnswer { let three = match ::try_from(3_u8) { Ok(three) => three, - Err(_) => return FizzBuzzAnswer::One(self.to_string()), + Err(_) => return FizzBuzzAnswer::Number(self.to_string()), }; let five = match ::try_from(5_u8) { Ok(five) => five, - Err(_) => return FizzBuzzAnswer::One(self.to_string()), + Err(_) => return FizzBuzzAnswer::Number(self.to_string()), }; let zero = match ::try_from(0_u8) { Ok(zero) => zero, - Err(_) => return FizzBuzzAnswer::One(self.to_string()), + Err(_) => return FizzBuzzAnswer::Number(self.to_string()), }; match (self % three == zero, self % five == zero) { - (true, true) => FizzBuzzAnswer::One("fizzbuzz".to_string()), - (true, false) => FizzBuzzAnswer::One("fizz".to_string()), - (false, true) => FizzBuzzAnswer::One("buzz".to_string()), - _ => FizzBuzzAnswer::One(self.to_string()), + (true, true) => FizzBuzzAnswer::Fizzbuzz, + (true, false) => FizzBuzzAnswer::Fizz, + (false, true) => FizzBuzzAnswer::Buzz, + _ => FizzBuzzAnswer::Number(self.to_string()), } } } -/// Used to obtain the correct `FizzBuzzAnswer` for a `Vec` of numbers -/// -/// ### Required: -/// - fn fizzbuzz(self) -> FizzBuzzAnswer +/// Used to obtain the correct `FizzBuzzAnswer` for a multiple fizzbuzz-able numbers pub trait MultiFizzBuzz { - fn fizzbuzz(self) -> FizzBuzzAnswer; + /// Returns an iterator which provides the FizzBuzz values for the elements of the implementing type. + /// + /// Note: + /// - This function **consumes** the input + /// - The returned iterator is a `rayon::iter::IndexedParallelIterator` + /// - The Items in the returned iterator will be converted to a requested type + /// (e.g. `FizzBuzzAnswer`, `String`, `Cow`) + fn fizzbuzz(self) -> impl IndexedParallelIterator + where + Rtn: From + Send; } +/// Implements the MultiFizzBuzz trait for any type which can be easily converted into a +/// `rayon::iter::IndexedParallelIterator` over Items which implement `fizzbuzz::FizzBuzz` +/// +/// Note: +/// - The returned iterator is _lazy_ - no calculations are performed until you use it +/// - Collecting this iterator requires that `rayon::iter::ParallelIterator` is in scope +/// - This implementation will decide whether it is worth the overhead of spawning multiple parallel threads impl MultiFizzBuzz for Iterable where Iterable: rayon::iter::IntoParallelIterator, ::Iter: IndexedParallelIterator, Num: FizzBuzz, { - fn fizzbuzz(self) -> FizzBuzzAnswer { + fn fizzbuzz(self) -> impl IndexedParallelIterator + where + Rtn: From + Send, + { 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(), - ) + let min_len = if par_iter.len() < BIG_VECTOR { + BIG_VECTOR //Don't parallelise when small } else { - FizzBuzzAnswer::Many(par_iter.map(|n| n.fizzbuzz().into()).collect()) - } + 1 + }; + par_iter.with_min_len(min_len).map(|n| n.fizzbuzz().into()) } } #[cfg(test)] mod test { - use super::*; - #[test] - fn vec_to_string() { - let input = FizzBuzzAnswer::Many(vec![ - "1".to_string(), - "2".to_string(), - "fizz".to_string(), - "4".to_string(), - "buzz".to_string(), - ]); - let output: String = input.into(); - let expected = "1, 2, fizz, 4, buzz".to_string(); - assert_eq!(output, expected) - } + use super::*; #[test] fn big_vector_is_well_ordered() { let input: Vec<_> = (1..BIG_VECTOR + 2).collect(); - let output: Vec<_> = input.clone().fizzbuzz().into(); - let mut expected: Vec = vec![]; + let output: Vec = input.clone().fizzbuzz().collect(); + let mut expected: Vec = vec![]; for i in input.iter() { - expected.push(i.fizzbuzz().into()) + expected.push(i.fizzbuzz()) } assert_eq!(output, expected); } @@ -156,11 +172,11 @@ mod test { #[test] fn fizzbuzz_range() { let input = 1..20; - let mut expected: Vec = vec![]; + let mut expected: Vec = vec![]; for i in 1..20 { expected.push(i.fizzbuzz().into()) } - let output: Vec = input.fizzbuzz().into(); + let output: Vec = input.fizzbuzz().collect(); assert_eq!(output, expected) } } diff --git a/rust/fizzbuzz/tests/test_numbertypes.rs b/rust/fizzbuzz/tests/test_fizzbuzz.rs similarity index 60% rename from rust/fizzbuzz/tests/test_numbertypes.rs rename to rust/fizzbuzz/tests/test_fizzbuzz.rs index 41bbbaf..34c13a0 100644 --- a/rust/fizzbuzz/tests/test_numbertypes.rs +++ b/rust/fizzbuzz/tests/test_fizzbuzz.rs @@ -1,3 +1,4 @@ +#![cfg(test)] use fizzbuzz::FizzBuzz; use googletest::prelude::*; @@ -32,7 +33,9 @@ macro_rules! test_this { } /// Test all compatible standard types -mod standard_types { +mod standard_types_up_to_127_as_strings { + use fizzbuzz::FizzBuzzAnswer; + use super::*; test_this! { @@ -52,23 +55,38 @@ mod standard_types { test_usize: usize } - // #[test] - // fn test_negative() { - // assert_eq!((-1_i8).fizzbuzz(), "-1"); - // assert_eq!((-3_i8).fizzbuzz(), "fizz"); - // assert_eq!((-5_i8).fizzbuzz(), "buzz"); - // assert_eq!((-15_i8).fizzbuzz(), "fizzbuzz"); - // } - - // #[test] - // fn test_not_whole_number() { - // assert_eq!(3.2_f32.fizzbuzz(), "3.2"); - // } + #[test] + fn test_negative() { + assert_eq!( + >::into((-1_i8).fizzbuzz()), + "-1".to_string() + ); + assert_eq!( + >::into((-3_i8).fizzbuzz()), + "fizz".to_string() + ); + assert_eq!( + >::into((-5_i8).fizzbuzz()), + "buzz".to_string() + ); + assert_eq!( + >::into((-15_i8).fizzbuzz()), + "fizzbuzz".to_string() + ); + } + + #[test] + fn test_not_whole_number() { + assert_eq!( + >::into((3.2_f32).fizzbuzz()), + "3.2".to_string() + ); + } } /// Create a custom type based on i16, add the minimum set of non-derivable /// traits, impl_fizzbuzz! and test ... -mod custom_types { +mod custom_types_as_strings { use std::{fmt::Display, ops::Rem}; use super::*; @@ -97,3 +115,37 @@ mod custom_types { my_int16: Myint } } + +mod ints_as_cows { + use std::borrow::Cow; + + use super::*; + + #[test] + fn number() { + let expected: Cow = "2".into(); + let answer: Cow = 2.fizzbuzz().into(); + assert_eq!(answer, expected) + } + + #[test] + fn fizz() { + let expected: Cow = "fizz".into(); + let answer: Cow = 3.fizzbuzz().into(); + assert_eq!(answer, expected) + } + + #[test] + fn buzz() { + let expected: Cow = "buzz".into(); + let answer: Cow = 5.fizzbuzz().into(); + assert_eq!(answer, expected) + } + + #[test] + fn fizzbuzz() { + let expected: Cow = "fizzbuzz".into(); + let answer: Cow = 15.fizzbuzz().into(); + assert_eq!(answer, expected) + } +} diff --git a/rust/fizzbuzz/tests/test_multifizzbuzz.rs b/rust/fizzbuzz/tests/test_multifizzbuzz.rs index 57ffa60..6b3fd75 100644 --- a/rust/fizzbuzz/tests/test_multifizzbuzz.rs +++ b/rust/fizzbuzz/tests/test_multifizzbuzz.rs @@ -1,4 +1,5 @@ use fizzbuzz::MultiFizzBuzz; +use rayon::iter::ParallelIterator; mod vectors { @@ -7,7 +8,7 @@ mod vectors { #[test] fn test_small_vec() { let input = vec![1, 2, 3, 4, 5]; - let answer: Vec = input.fizzbuzz().into(); + let answer: Vec = input.fizzbuzz().collect(); let expected = vec![ "1".to_string(), "2".to_string(), @@ -17,6 +18,14 @@ mod vectors { ]; assert_eq!(answer, expected) } + + #[test] + fn test_empty_vector() { + let input: Vec = vec![]; + let answer: Vec = input.fizzbuzz().collect(); + let expected: Vec = vec![]; + assert_eq!(answer, expected); + } } mod ranges { @@ -26,7 +35,7 @@ mod ranges { #[test] fn test_small_range() { - let answer: Vec = (1..=5_i16).fizzbuzz().into(); + let answer: Vec = (1..=5_i16).fizzbuzz().collect(); let expected = vec![ "1".to_string(), "2".to_string(), @@ -40,7 +49,7 @@ mod ranges { #[test] fn test_range_with_step() { let input = (0..16).into_par_iter().step_by(3); - let answer: Vec = input.fizzbuzz().into(); + let answer: Vec = input.fizzbuzz().collect(); let expected = vec![ "fizzbuzz".to_string(), // 0 "fizz".to_string(), // 3 diff --git a/rust/fizzbuzzo3/src/lib.rs b/rust/fizzbuzzo3/src/lib.rs index 8751459..c4b50a7 100644 --- a/rust/fizzbuzzo3/src/lib.rs +++ b/rust/fizzbuzzo3/src/lib.rs @@ -1,4 +1,4 @@ -use std::ops::Neg; +use std::{borrow::Cow, ops::Neg}; use fizzbuzz::{FizzBuzz, FizzBuzzAnswer, MultiFizzBuzz}; use pyo3::{exceptions::PyValueError, prelude::*, types::PySlice}; @@ -26,19 +26,22 @@ impl IntoPy> for MySlice { } /// A wrapper struct for FizzBuzzAnswer to provide a custom implementation of `IntoPy`. -struct FizzBuzzReturn(FizzBuzzAnswer); +enum FizzBuzzReturn { + One(String), + Many(Vec>), +} impl From for FizzBuzzReturn { fn from(value: FizzBuzzAnswer) -> Self { - FizzBuzzReturn(value) + FizzBuzzReturn::One(value.into()) } } impl IntoPy> for FizzBuzzReturn { fn into_py(self, py: Python<'_>) -> Py { - match self.0 { - FizzBuzzAnswer::One(string) => string.into_py(py), - FizzBuzzAnswer::Many(list) => list.into_py(py), + match self { + FizzBuzzReturn::One(answer) => answer.into_py(py), + FizzBuzzReturn::Many(answers) => answers.into_py(py), } } } @@ -92,19 +95,21 @@ fn py_fizzbuzz(num: FizzBuzzable) -> PyResult { match num { FizzBuzzable::Int(n) => Ok(n.fizzbuzz().into()), FizzBuzzable::Float(n) => Ok(n.fizzbuzz().into()), - FizzBuzzable::Vec(v) => Ok(v.fizzbuzz().into()), + FizzBuzzable::Vec(v) => Ok(FizzBuzzReturn::Many(v.fizzbuzz().collect())), FizzBuzzable::Slice(s) => match s.step { // Can only be tested from python: Cannot create a PySlice with no step in rust. - None => Ok((s.start..s.stop).fizzbuzz().into()), // GRCOV_EXCL_LINE + None => Ok(FizzBuzzReturn::Many((s.start..s.stop).fizzbuzz().collect())), // GRCOV_EXCL_LINE - Some(1) => Ok((s.start..s.stop).fizzbuzz().into()), + Some(1) => Ok(FizzBuzzReturn::Many((s.start..s.stop).fizzbuzz().collect())), Some(step) => match step { - 1.. => Ok((s.start..s.stop) - .into_par_iter() - .step_by(step.try_into().unwrap()) - .fizzbuzz() - .into()), + 1.. => Ok(FizzBuzzReturn::Many( + (s.start..s.stop) + .into_par_iter() + .step_by(step.try_into().unwrap()) + .fizzbuzz() + .collect(), + )), // ```python // >>> foo[1:5:0] @@ -120,12 +125,14 @@ fn py_fizzbuzz(num: FizzBuzzable) -> PyResult { // [6, 4, 2] // ``` // Rust doesn't accept step < 0 or stop < start so need some trickery - ..=-1 => Ok((s.start.neg()..s.stop.neg()) - .into_par_iter() - .step_by(step.neg().try_into().unwrap()) - .map(|x| x.neg()) - .fizzbuzz() - .into()), + ..=-1 => Ok(FizzBuzzReturn::Many( + (s.start.neg()..s.stop.neg()) + .into_par_iter() + .step_by(step.neg().try_into().unwrap()) + .map(|x| x.neg()) + .fizzbuzz() + .collect(), + )), }, }, } diff --git a/tests/perf_results.md b/tests/perf_results.md index 4769fa0..07e232e 100644 --- a/tests/perf_results.md +++ b/tests/perf_results.md @@ -184,3 +184,33 @@ Rust vector, with python list overhead: [1 calls of 10 runs creating and fizzbuz Rust range: [1 calls of 10 runs fizzbuzzing a range of numbers up to 10000000] [9.859676376989228] ``` + +## Using `Cow` to avoid memory allocations + +Is no faster: + +### Cow + +```text +Rust: [1 calls of 10 runs fizzbuzzing up to 1000000] +[1.9146641360021022] +Rust vector: [1 calls of 10 runs fizzbuzzing a list of numbers up to 1000000] +[1.0816771969984984] +Rust vector, with python list overhead: [1 calls of 10 runs creating and fizzbuzzing a list of numbers up to 1000000] +[1.141931141999521] +Rust range: [1 calls of 10 runs fizzbuzzing a range of numbers up to 1000000] +[0.7313544280004862] +``` + +### No Cow + +```text +Rust: [1 calls of 10 runs fizzbuzzing up to 1000000] +[1.9076293110010738] +Rust vector: [1 calls of 10 runs fizzbuzzing a list of numbers up to 1000000] +[0.913994423000986] +Rust vector, with python list overhead: [1 calls of 10 runs creating and fizzbuzzing a list of numbers up to 1000000] +[1.1336928130003798] +Rust range: [1 calls of 10 runs fizzbuzzing a range of numbers up to 1000000] +[0.739404623000155] +```