cycle_ptr 0.1.1

Smart pointers, with cycles
Documentation
//! Benchmarks for cycle pointer (the ones that are thread-local).

use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main};
use cycle_ptr::prelude::*;
use cycle_ptr::{GcMemberPtr, GcPtr, GcTask, GenerationRef, Metadata};
use std::cell::RefCell;
use std::time::Duration;

/// We use a parameterized benchmark, to confirm that the cache timings remain mostly-constant regardless of the cache size.
const SIZES: [usize; 10] = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];

/// Timing tests for constructor.
fn constructor(c: &mut Criterion) {
    struct Object;

    c.bench_function("constructor", |b| {
        b.iter_with_large_drop(|| GcPtr::new(|_| Object));
    });
}

/// Timing tests for destructor of non-cyclic pointers.
fn destructor(c: &mut Criterion) {
    struct Object;

    c.bench_function("destructor", |b| {
        b.iter_batched(
            || GcPtr::new(|_| Object),
            |ptr: GcPtr<Object>| drop(ptr),
            BatchSize::SmallInput,
        );
    });
}

/// Timing tests for constructor, when there are many cycles involved.
fn constructor_with_cycles(c: &mut Criterion) {
    struct Object {
        _parent: GcMemberPtr<ObjectCollection>,
    }

    struct ObjectCollection {
        gc_metadata: Metadata,
        data: RefCell<Vec<GcMemberPtr<Object>>>,
    }

    let mut group = c.benchmark_group("constructor_with_cycles");
    for size in SIZES.into_iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
            b.iter_batched_ref(
                || {
                    let collection = GcPtr::new(|gc_metadata| ObjectCollection {
                        gc_metadata,
                        data: Vec::with_capacity(size).into(),
                    });
                    for _ in 0..size {
                        let object = GcPtr::new(|gc_metadata| Object {
                            _parent: gc_metadata.new_pointer(collection.clone()),
                        });
                        collection
                            .data
                            .borrow_mut()
                            .push(collection.gc_metadata.new_pointer(object));
                    }
                    collection
                },
                |collection| {
                    let object = GcPtr::new(|gc_metadata| Object {
                        _parent: gc_metadata.new_pointer(collection.clone()),
                    });
                    collection
                        .data
                        .borrow_mut()
                        .push(collection.gc_metadata.new_pointer(object));
                },
                BatchSize::SmallInput,
            );
        });
    }
    group.finish();
}

/// Timing tests for constructor.
fn constructor_with_cycles_using_generation_ref(c: &mut Criterion) {
    struct Object {
        _parent: GcMemberPtr<ObjectCollection>,
    }

    struct ObjectCollection {
        gc_metadata: Metadata,
        data: RefCell<Vec<GcMemberPtr<Object>>>,
    }

    let mut group = c.benchmark_group("constructor_with_cycles_using_generation_ref");
    for size in SIZES.into_iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
            b.iter_batched_ref(
                || {
                    let generation = GenerationRef::default();
                    let collection = generation.make(|gc_metadata| ObjectCollection {
                        gc_metadata,
                        data: Vec::with_capacity(size).into(),
                    });
                    for _ in 0..size {
                        let object = generation.make(|gc_metadata| Object {
                            _parent: gc_metadata.new_pointer(collection.clone()),
                        });
                        collection
                            .data
                            .borrow_mut()
                            .push(collection.gc_metadata.new_pointer(object));
                    }
                    (generation, collection)
                },
                |(generation, collection)| {
                    let object = generation.make(|gc_metadata| Object {
                        _parent: gc_metadata.new_pointer(collection.clone()),
                    });
                    collection
                        .data
                        .borrow_mut()
                        .push(collection.gc_metadata.new_pointer(object));
                },
                BatchSize::SmallInput,
            );
        });
    }
    group.finish();
}

fn gc_without_cycles(c: &mut Criterion) {
    struct Object;

    struct ObjectCollection {
        gc_metadata: Metadata,
        data: RefCell<Vec<GcMemberPtr<Object>>>,
    }

    let mut group = c.benchmark_group("gc_without_cycles");
    for size in SIZES.into_iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
            b.iter_batched(
                || {
                    let collection = GcPtr::new(|gc_metadata| ObjectCollection {
                        gc_metadata,
                        data: Vec::with_capacity(size).into(),
                    });
                    for _ in 0..size {
                        collection
                            .data
                            .borrow_mut()
                            .push(collection.gc_metadata.new_pointer(GcPtr::new(|_| Object)));
                    }
                    collection
                },
                |collection: GcPtr<ObjectCollection>| drop(collection),
                BatchSize::SmallInput,
            );
        });
    }
    group.finish();
}

fn gc_with_cycles(c: &mut Criterion) {
    struct Object {
        _parent: GcMemberPtr<ObjectCollection>,
    }

    struct ObjectCollection {
        gc_metadata: Metadata,
        data: RefCell<Vec<GcMemberPtr<Object>>>,
    }

    let mut group = c.benchmark_group("gc_with_cycles");
    for size in SIZES.into_iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
            b.iter_batched(
                || {
                    let gc_generation = GenerationRef::default();
                    let collection = gc_generation.make(|gc_metadata| ObjectCollection {
                        gc_metadata,
                        data: Vec::with_capacity(size).into(),
                    });
                    for _ in 0..size {
                        let object = gc_generation.make(|gc_metadata| Object {
                            _parent: gc_metadata.new_pointer(collection.clone()),
                        });
                        collection
                            .data
                            .borrow_mut()
                            .push(collection.gc_metadata.new_pointer(object));
                    }
                    collection
                },
                |collection: GcPtr<ObjectCollection>| drop(collection),
                BatchSize::SmallInput,
            );
        });
    }
    group.finish();
}

fn gc_with_cycles_and_deferred_gc(c: &mut Criterion) {
    struct Object {
        _parent: GcMemberPtr<ObjectCollection>,
    }

    struct ObjectCollection {
        gc_metadata: Metadata,
        data: RefCell<Vec<GcMemberPtr<Object>>>,
    }

    let mut group = c.benchmark_group("gc_with_cycles_and_deferred_gc");
    for size in SIZES.into_iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
            b.iter_batched(
                || {
                    let gc_generation = GenerationRef::default();
                    let collection = gc_generation.make(|gc_metadata| ObjectCollection {
                        gc_metadata,
                        data: Vec::with_capacity(size).into(),
                    });
                    for _ in 0..size {
                        let object = gc_generation.make(|gc_metadata| Object {
                            _parent: gc_metadata.new_pointer(collection.clone()),
                        });
                        collection
                            .data
                            .borrow_mut()
                            .push(collection.gc_metadata.new_pointer(object));
                    }
                    collection
                },
                |collection: GcPtr<ObjectCollection>| {
                    // Defer GC tasks.
                    let mut gc = Vec::default();
                    let handle = GcTask::install_callback(|task| gc.push(task));

                    collection.data.borrow_mut().clear(); // adds each value to the GC.
                    drop(collection); // adds the collection to the GC.

                    // Execute deferred GC tasks.
                    drop(handle);
                    gc.into_iter().for_each(|task| {
                        task.run();
                    });
                },
                BatchSize::SmallInput,
            );
        });
    }
    group.finish();
}

criterion_group!(
    name = benches;
    config = Criterion::default().configure_from_args().noise_threshold(0.025);
    targets = constructor, destructor, constructor_with_cycles_using_generation_ref,
);
criterion_group!(
    name = slow_benches;
    config = Criterion::default().configure_from_args().measurement_time(Duration::from_secs(10)).sample_size(50).noise_threshold(0.025);
    targets = constructor_with_cycles, gc_without_cycles, gc_with_cycles, gc_with_cycles_and_deferred_gc,
);
criterion_main!(benches, slow_benches);