memory-cache-rust 0.1.0-alpha

memory-cache is a fast, concurrent cache library built with a focus on performance and correctness. The motivation to build Ristretto comes from the need for a contention-free cache in
Documentation
/* Benchmarks from `hashbrown` (https://github.com/rust-lang/hashbrown),
 * adapted to flurry for comparison:
 *
 * This benchmark suite contains some benchmarks along a set of dimensions:
 *   Int key distribution: low bit heavy, top bit heavy, and random.
 *   Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter
 *
 * For the associated license information, please refer to hashbrown.LICENSE.
 */

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};

use memory_cache_rust::cache::{Cache};

const SIZE: usize = 1000;

#[derive(Clone, Copy)]
struct RandomKeys {
    state: usize,
}

impl RandomKeys {
    fn new() -> Self {
        RandomKeys { state: 0 }
    }
}

impl Iterator for RandomKeys {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        // Add 1 then multiply by some 32 bit prime.
        self.state = self.state.wrapping_add(1).wrapping_mul(3_787_392_781);
        Some(self.state)
    }
}

macro_rules! bench_suite {
    ($bench_macro:ident, $bench_fn_name:ident, $group_name:expr $(,)?) => {
        fn $bench_fn_name(c: &mut Criterion) {
            let mut group = c.benchmark_group($group_name);
            group.throughput(Throughput::Elements(SIZE as u64));

            $bench_macro!(group, 0.., "low_guard_once");
            $bench_macro!(group, (0..).map(usize::swap_bytes), "high_guard_once");
            $bench_macro!(group, RandomKeys::new(), "random_guard_once");

            group.finish();
        }
    };
}

macro_rules! bench_insert {
    ($group:ident, $keydist:expr, $bench_id: expr) => {
        $group.bench_function(BenchmarkId::from_parameter($bench_id), |b| {
            let map: Cache<u64, u64> =  Cache::new();
            b.iter(|| {
                let guard = map.guard();
                map.clear(&guard);
                ($keydist).take(SIZE).for_each(|i| {
                    map.set(i as u64, i as u64, 0,&guard);
                });
                black_box(&map);
            });
        });
    };
}

bench_suite!(
    bench_insert,
    insert_flurry_hashbrown,
    "insert_flurry_hashbrown",
);

macro_rules! bench_insert_erase {
    ($group:ident, $keydist:expr, $bench_id: expr) => {
        let base: Cache<u64, u64> = Cache::new();
        {
            // NOTE: in testing, I tried running this without the local scope.
            // not dropping the guard and pinning the epoch for the entire benchmark literally
            // crashed multiple programs on my PC, so I advise not to do that...
            let guard = base.guard();
            ($keydist).take(SIZE).for_each(|i| {
                base.set(i as u64 , i as u64,0, &guard);
            });
        }
        let skip = ($keydist).take(SIZE);

        $group.bench_function(BenchmarkId::from_parameter($bench_id), |b| {
            b.iter(|| {
                let mut map = base.clone();
                let mut add_iter = skip.clone();
                let mut remove_iter = $keydist;

                // While keeping the size constant,
                // replace the first keydist with the second.
                let guard = map.guard();
                (&mut add_iter)
                    .zip(&mut remove_iter)
                    .take(SIZE)
                    .for_each(|(add, remove)| {
                        map.set(add as u64, add as u64,0, &guard);
                        black_box(map.del(&(remove as u64) , &guard));
                    });
                drop(guard);
                black_box(&mut map);
            });
        });
    };
}

bench_suite!(
    bench_insert_erase,
    insert_erase_flurry_hashbrown,
    "insert_erase_flurry_hashbrown",
);

macro_rules! bench_lookup {
    ($group:ident, $keydist:expr, $bench_id: expr) => {
        let map: Cache<u64, u64> =  Cache::new();
        {
            // see bench_insert_erase for a comment on the local scope
            let guard = map.guard();
            ($keydist).take(SIZE).for_each(|i| {
                map.set(i as u64, i as u64,0, &guard);
            });
        }

        $group.bench_function(BenchmarkId::from_parameter($bench_id), |b| {
            b.iter(|| {
                let guard = map.guard();
                ($keydist).take(SIZE).for_each(|i| {
                    black_box(map.get(&(i as u64), &guard));
                });
            });
        });
    };
}

bench_suite!(bench_lookup, get_flurry_hashbrown, "get_flurry_hashbrown",);

macro_rules! bench_lookup_fail {
    ($group:ident, $keydist:expr, $bench_id: expr) => {
        let map: Cache<u64, u64> = Cache::new();
        let mut iter = $keydist;
        {
            // see bench_insert_erase for a comment on the local scope
            let guard = map.guard();
            (&mut iter).take(SIZE).for_each(|i| {
                map.set(i as u64 , i as u64,0, &guard);
            });
        }

        $group.bench_function(BenchmarkId::from_parameter($bench_id), |b| {
            b.iter(|| {
                let guard = map.guard();
                (&mut iter).take(SIZE).for_each(|i| {
                    black_box(map.get(&(i as u64), &guard));
                });
            });
        });
    };
}

bench_suite!(
    bench_lookup_fail,
    get_absent_flurry_hashbrown,
    "get_absent_flurry_hashbrown",
);

macro_rules! bench_iter {
    ($group:ident, $keydist:expr, $bench_id: expr) => {
        let map: Cache<u64, u64> = Cache::new();
        {
            // see bench_insert_erase for a comment on the local scope
            let guard = map.guard();
            ($keydist).take(SIZE).for_each(|i| {
                map.set(i as u64, i as u64,0, &guard);
            });
        }

       /* $group.bench_function(BenchmarkId::from_parameter($bench_id), |b| {
            b.iter(|| {
                let guard = map.guard();
                for k in map.iter(&guard) {
                    black_box(k);
                }
            });
        });*/
    };
}

bench_suite!(bench_iter, iter_flurry_hashbrown, "iter_flurry_hashbrown",);

criterion_group!(
    benches,
    insert_flurry_hashbrown,
    // insert_erase_flurry_hashbrown,
    // get_flurry_hashbrown,
    // get_absent_flurry_hashbrown,
    // iter_flurry_hashbrown,
);
criterion_main!(benches);