I have created the following simple Fibonacci implementations:
#![feature(test)]
extern crate test;
pub fn fibonacci_recursive(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
}
}
pub fn fibonacci_imperative(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => {
let mut penultimate;
let mut last = 1;
let mut fib = 0;
for _ in 0..n {
penultimate = last;
last = fib;
fib = penultimate + last;
}
fib
}
}
}
I created them to try out cargo bench
, so I wrote the following benchmarks:
#[cfg(test)]
mod tests {
use super::*;
use test::Bencher;
#[bench]
fn bench_fibonacci_recursive(b: &mut Bencher) {
b.iter(|| {
let n = test::black_box(20);
fibonacci_recursive(n)
});
}
#[bench]
fn bench_fibonacci_imperative(b: &mut Bencher) {
b.iter(|| {
let n = test::black_box(20);
fibonacci_imperative(n)
});
}
}
I know that a recursive implementation is generally slower than an imperative one, especially since Rust doesn't support tail recursion optimization (which this implementation couldn't use anyways). But I was not expecting the following difference of nearly 2'000 times:
running 2 tests
test tests::bench_fibonacci_imperative ... bench: 15 ns/iter (+/- 3)
test tests::bench_fibonacci_recursive ... bench: 28,435 ns/iter (+/- 1,114)
I ran it both on Windows and Ubuntu with the newest Rust nightly compiler (rustc 1.25.0-nightly
) and obtained similar results.
Is this speed difference normal? Did I write something "wrong"? Or are my benchmarks flawed?