Use bench tests to optimize factorial and fibonacci functions
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good
This commit is contained in:
parent
75fe940ffc
commit
3624b415ee
@ -1,40 +1,32 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use rusty_numbers::{factorial, fibonacci, rec_factorial, rec_fibonacci};
|
use rusty_numbers::{factorial, fibonacci, mem_fibonacci, mem_factorial};
|
||||||
|
|
||||||
fn bench_factorial(c: &mut Criterion) {
|
fn bench_factorial(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("Factorials");
|
let mut group = c.benchmark_group("Factorials");
|
||||||
|
|
||||||
// Recursive with lookup table
|
for i in [0usize, 5, 10, 15, 20, 25, 30, 34].iter() {
|
||||||
group.bench_function("factorial", |b| b.iter(|| factorial(34)));
|
group.bench_with_input(BenchmarkId::new("Recursive memoized", i), i, |b, i| {
|
||||||
group.bench_function("factorial black box", |b| {
|
b.iter(|| mem_factorial(*i))
|
||||||
b.iter(|| factorial(black_box(34)))
|
|
||||||
});
|
});
|
||||||
|
group.bench_with_input(BenchmarkId::new("Recursive naive", i), i, |b, i| {
|
||||||
// Naive recursive
|
b.iter(|| factorial(*i))
|
||||||
group.bench_function("rec_factorial", |b| b.iter(|| rec_factorial(34)));
|
|
||||||
group.bench_function("rec_factorial black box", |b| {
|
|
||||||
b.iter(|| rec_factorial(black_box(34)))
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_fibonacci(c: &mut Criterion) {
|
fn bench_fibonacci(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("Fibonacci");
|
let mut group = c.benchmark_group("Fibonacci");
|
||||||
|
|
||||||
// Recursive with lookup table
|
for i in [0usize, 5, 10, 15, 20, 30].iter() {
|
||||||
group.bench_function("fibonacci", |b| b.iter(|| fibonacci(86)));
|
group.bench_with_input(BenchmarkId::new("Recursive memoized", i), i, |b, i| {
|
||||||
group.bench_function("fibonacci black box", |b| {
|
b.iter(|| mem_fibonacci(*i))
|
||||||
b.iter(|| fibonacci(black_box(86)))
|
|
||||||
});
|
});
|
||||||
|
// group.bench_with_input(BenchmarkId::new("Recursive naive", i), i, |b, i| b.iter(|| rec_fibonacci(*i)));
|
||||||
// Naive recursive
|
group.bench_with_input(BenchmarkId::new("Iterative", i), i, |b, i| {
|
||||||
group.bench_function("rec_fibonacci", |b| b.iter(|| rec_fibonacci(86)));
|
b.iter(|| fibonacci(*i))
|
||||||
group.bench_function("rec_fibonacci black box", |b| {
|
|
||||||
b.iter(|| rec_fibonacci(black_box(86)))
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
group.finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, bench_factorial, bench_fibonacci);
|
criterion_group!(benches, bench_factorial, bench_fibonacci);
|
||||||
|
113
src/lib.rs
113
src/lib.rs
@ -25,36 +25,42 @@ pub mod rational;
|
|||||||
/// # assert!(invalid.is_none());
|
/// # assert!(invalid.is_none());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fibonacci(n: usize) -> Option<u128> {
|
pub fn mem_fibonacci(n: usize) -> Option<u128> {
|
||||||
let mut table: Vec<u128> = vec![0, 1, 1, 2, 3, 5];
|
let mut table = [0u128; 187];
|
||||||
|
table[0] = 0;
|
||||||
|
table[1] = 1;
|
||||||
|
table[2] = 1;
|
||||||
|
|
||||||
_fibonacci(n, &mut table)
|
_mem_fibonacci(n, &mut table)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actual calculating function for `fibonacci`
|
/// Actual calculating function for `fibonacci`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn _fibonacci(n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
fn _mem_fibonacci(n: usize, table: &mut [u128]) -> Option<u128> {
|
||||||
match table.get(n) {
|
if n > 186 {
|
||||||
Some(x) => Some(*x),
|
return None;
|
||||||
None => {
|
|
||||||
let a = _fibonacci(n - 1, table)?;
|
|
||||||
let b = _fibonacci(n - 2, table)?;
|
|
||||||
|
|
||||||
// Check for overflow when adding
|
|
||||||
let attempt = a.checked_add(b);
|
|
||||||
|
|
||||||
if let Some(current) = attempt {
|
|
||||||
table.insert(n, current);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt
|
match table[n] {
|
||||||
}
|
// The lookup array starts out zeroed, so a zero
|
||||||
|
// is a not yet calculated value
|
||||||
|
0 => {
|
||||||
|
let a = _mem_fibonacci(n - 1, table)?;
|
||||||
|
let b = _mem_fibonacci(n - 2, table)?;
|
||||||
|
|
||||||
|
table[n] = a + b;
|
||||||
|
|
||||||
|
Some(table[n])
|
||||||
|
},
|
||||||
|
x => Some(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a number in the fibonacci sequence,
|
/// Calculate a number in the fibonacci sequence,
|
||||||
/// using naive recursion
|
/// using naive recursion
|
||||||
///
|
///
|
||||||
|
/// REALLY SLOW
|
||||||
|
///
|
||||||
/// Can calculate up to 186 using native unsigned 128 bit integers.
|
/// Can calculate up to 186 using native unsigned 128 bit integers.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rec_fibonacci(n: usize) -> Option<u128> {
|
pub fn rec_fibonacci(n: usize) -> Option<u128> {
|
||||||
@ -66,12 +72,16 @@ pub fn rec_fibonacci(n: usize) -> Option<u128> {
|
|||||||
let b = rec_fibonacci(n - 2)?;
|
let b = rec_fibonacci(n - 2)?;
|
||||||
|
|
||||||
a.checked_add(b)
|
a.checked_add(b)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate a number in the fibonacci sequence,
|
||||||
|
/// using iteration
|
||||||
|
///
|
||||||
|
/// Can calculate up to 186
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn it_fibonacci(n: usize) -> Option<u128> {
|
pub fn fibonacci(n: usize) -> Option<u128> {
|
||||||
let mut a: u128 = 0;
|
let mut a: u128 = 0;
|
||||||
let mut b: u128 = 1;
|
let mut b: u128 = 1;
|
||||||
|
|
||||||
@ -107,49 +117,60 @@ pub fn it_fibonacci(n: usize) -> Option<u128> {
|
|||||||
/// # assert!(invalid.is_none());
|
/// # assert!(invalid.is_none());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn factorial(n: usize) -> Option<u128> {
|
pub fn mem_factorial(n: usize) -> Option<u128> {
|
||||||
let mut table: Vec<u128> = vec![1, 1, 2];
|
let mut table = [0u128; 35];
|
||||||
|
table[0] = 1;
|
||||||
|
table[1] = 1;
|
||||||
|
table[2] = 2;
|
||||||
|
|
||||||
_factorial(n, &mut table)
|
|
||||||
|
_mem_factorial(n, &mut table)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actual Calculation function for factoral
|
/// Actual Calculation function for factoral
|
||||||
#[inline]
|
#[inline]
|
||||||
fn _factorial(n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
fn _mem_factorial(n: usize, table: &mut [u128]) -> Option<u128> {
|
||||||
match table.get(n) {
|
if n > 34 {
|
||||||
// Vec<T>.get returns a Option with a reference to the value,
|
return None
|
||||||
// so deref and wrap in Some() for proper return type
|
}
|
||||||
Some(x) => Some(*x),
|
|
||||||
None => {
|
match table[n] {
|
||||||
|
// Empty spaces in the lookup array are zero-filled
|
||||||
|
0 => {
|
||||||
// The ? suffix passes along the Option value
|
// The ? suffix passes along the Option value
|
||||||
// to be handled later
|
// to be handled later
|
||||||
// See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
|
// See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
|
||||||
let prev = _factorial(n - 1, table)?;
|
let prev = _mem_factorial(n - 1, table)?;
|
||||||
|
|
||||||
// Do an overflow-checked multiply
|
table[n] = (n as u128) * prev;
|
||||||
let attempt = (n as u128).checked_mul(prev);
|
|
||||||
|
|
||||||
// If there isn't an overflow, add the result
|
Some(table[n])
|
||||||
// to the calculation table
|
},
|
||||||
if let Some(current) = attempt {
|
x => Some(x),
|
||||||
table.insert(n, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
attempt // Some(x) if no overflow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the value of a factorial using recursion
|
/// Calculate the value of a factorial,
|
||||||
///
|
///
|
||||||
/// Can calculate up to 34! using native unsigned 128 bit integers.
|
/// Can calculate up to 34! using native unsigned 128 bit integers.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```rust
|
||||||
|
/// use rusty_numbers::factorial;
|
||||||
|
///
|
||||||
|
/// let valid = factorial(3); // Some(6)
|
||||||
|
/// # assert_eq!(6, valid.unwrap());
|
||||||
|
///
|
||||||
|
/// let invalid = factorial(35); // None
|
||||||
|
/// # assert!(invalid.is_none());
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rec_factorial(n: usize) -> Option<u128> {
|
pub fn factorial(n: usize) -> Option<u128> {
|
||||||
match n {
|
match n {
|
||||||
0 => Some(1u128),
|
0 => Some(1u128),
|
||||||
1 => Some(1u128),
|
1 => Some(1u128),
|
||||||
_ => {
|
_ => {
|
||||||
let prev = rec_factorial(n - 1)?;
|
let prev = factorial(n - 1)?;
|
||||||
|
|
||||||
(n as u128).checked_mul(prev)
|
(n as u128).checked_mul(prev)
|
||||||
}
|
}
|
||||||
@ -168,7 +189,7 @@ mod tests {
|
|||||||
assert_eq!(6, factorial(3).unwrap());
|
assert_eq!(6, factorial(3).unwrap());
|
||||||
|
|
||||||
let res = factorial(34);
|
let res = factorial(34);
|
||||||
let res2 = rec_factorial(34);
|
let res2 = mem_factorial(34);
|
||||||
assert!(res.is_some());
|
assert!(res.is_some());
|
||||||
assert_eq!(res.unwrap(), res2.unwrap());
|
assert_eq!(res.unwrap(), res2.unwrap());
|
||||||
|
|
||||||
@ -182,8 +203,12 @@ mod tests {
|
|||||||
assert_eq!(1, fibonacci(1).unwrap());
|
assert_eq!(1, fibonacci(1).unwrap());
|
||||||
assert_eq!(1, fibonacci(2).unwrap());
|
assert_eq!(1, fibonacci(2).unwrap());
|
||||||
|
|
||||||
|
let res = fibonacci(20);
|
||||||
|
let res2 = rec_fibonacci(20);
|
||||||
|
assert_eq!(res, res2);
|
||||||
|
|
||||||
let res = fibonacci(186);
|
let res = fibonacci(186);
|
||||||
let res2 = it_fibonacci(186);
|
let res2 = mem_fibonacci(186);
|
||||||
assert!(res.is_some());
|
assert!(res.is_some());
|
||||||
assert!(res2.is_some());
|
assert!(res2.is_some());
|
||||||
assert_eq!(res.unwrap(), res2.unwrap());
|
assert_eq!(res.unwrap(), res2.unwrap());
|
||||||
|
Loading…
Reference in New Issue
Block a user