use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use light_clone::LightClone;
use std::hint::black_box;
use std::sync::Arc;
#[derive(Clone)]
#[allow(dead_code)]
struct LargeStruct {
data: [u8; 4096],
values: [i64; 512],
}
impl Default for LargeStruct {
fn default() -> Self {
Self {
data: [0u8; 4096],
values: [0i64; 512],
}
}
}
#[derive(Clone, LightClone)]
#[allow(dead_code)]
struct FiveArcFields {
field1: Arc<str>,
field2: Arc<str>,
field3: Arc<str>,
field4: Arc<str>,
field5: Arc<str>,
}
impl FiveArcFields {
fn new(s: &str) -> Self {
let arc_s: Arc<str> = Arc::from(s);
Self {
field1: arc_s.clone(),
field2: arc_s.clone(),
field3: arc_s.clone(),
field4: arc_s.clone(),
field5: arc_s,
}
}
}
#[derive(Clone)]
#[allow(dead_code)]
struct FiveStringFields {
field1: String,
field2: String,
field3: String,
field4: String,
field5: String,
}
impl FiveStringFields {
fn new(s: &str) -> Self {
Self {
field1: s.to_string(),
field2: s.to_string(),
field3: s.to_string(),
field4: s.to_string(),
field5: s.to_string(),
}
}
}
#[derive(Clone, LightClone)]
#[allow(dead_code)]
struct DeepNestedLc {
value: Arc<str>,
inner: Option<Arc<DeepNestedLc>>,
}
impl DeepNestedLc {
fn new(depth: usize, s: &str) -> Self {
let arc_s: Arc<str> = Arc::from(s);
let mut current = DeepNestedLc {
value: arc_s.clone(),
inner: None,
};
for _ in 1..depth {
current = DeepNestedLc {
value: arc_s.clone(),
inner: Some(Arc::new(current)),
};
}
current
}
}
#[derive(Clone)]
#[allow(dead_code)]
struct DeepNestedString {
value: String,
inner: Option<Box<DeepNestedString>>,
}
impl DeepNestedString {
fn new(depth: usize, s: &str) -> Self {
let mut current = DeepNestedString {
value: s.to_string(),
inner: None,
};
for _ in 1..depth {
current = DeepNestedString {
value: s.to_string(),
inner: Some(Box::new(current)),
};
}
current
}
}
#[derive(Clone, LightClone)]
#[allow(dead_code)]
struct NestedLevel1Lc {
value: Arc<str>,
count: i64,
}
#[derive(Clone, LightClone)]
#[allow(dead_code)]
struct NestedLevel2Lc {
inner: NestedLevel1Lc,
name: Arc<str>,
}
#[derive(Clone, LightClone)]
#[allow(dead_code)]
struct NestedLevel3Lc {
inner: NestedLevel2Lc,
data: Arc<str>,
}
impl NestedLevel3Lc {
fn new(s: &str) -> Self {
let arc_s: Arc<str> = Arc::from(s);
Self {
inner: NestedLevel2Lc {
inner: NestedLevel1Lc {
value: arc_s.clone(),
count: 42,
},
name: arc_s.clone(),
},
data: arc_s,
}
}
}
#[derive(Clone)]
#[allow(dead_code)]
struct NestedLevel1String {
value: String,
count: i64,
}
#[derive(Clone)]
#[allow(dead_code)]
struct NestedLevel2String {
inner: NestedLevel1String,
name: String,
}
#[derive(Clone)]
#[allow(dead_code)]
struct NestedLevel3String {
inner: NestedLevel2String,
data: String,
}
impl NestedLevel3String {
fn new(s: &str) -> Self {
Self {
inner: NestedLevel2String {
inner: NestedLevel1String {
value: s.to_string(),
count: 42,
},
name: s.to_string(),
},
data: s.to_string(),
}
}
}
fn make_string(size_bytes: usize) -> String {
"x".repeat(size_bytes)
}
fn bench_arc_str(c: &mut Criterion) {
let arc_str: Arc<str> = Arc::from("hello, world!");
let mut group = c.benchmark_group("arc_str");
group.bench_function("lc", |b| b.iter(|| black_box(arc_str.light_clone())));
group.bench_function("clone", |b| b.iter(|| black_box(arc_str.clone())));
group.finish();
}
fn bench_arc_large_struct(c: &mut Criterion) {
let arc_large: Arc<LargeStruct> = Arc::new(LargeStruct::default());
let mut group = c.benchmark_group("arc_large_struct");
group.bench_function("lc", |b| b.iter(|| black_box(arc_large.light_clone())));
group.bench_function("clone", |b| b.iter(|| black_box(arc_large.clone())));
group.finish();
}
fn bench_five_arc_fields(c: &mut Criterion) {
let five_arc = FiveArcFields::new("benchmark test string");
let mut group = c.benchmark_group("five_arc_fields");
group.bench_function("lc", |b| b.iter(|| black_box(five_arc.light_clone())));
group.bench_function("clone", |b| b.iter(|| black_box(five_arc.clone())));
group.finish();
}
fn bench_lc_vs_string_fields(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("lc_vs_string_fields_by_len");
for size in sizes {
let s = make_string(size);
let lc_struct = FiveArcFields::new(&s);
let string_struct = FiveStringFields::new(&s);
group.bench_with_input(
BenchmarkId::new("arc_lc", size),
&lc_struct,
|b, struct_ref| b.iter(|| black_box(struct_ref.light_clone())),
);
group.bench_with_input(
BenchmarkId::new("string_clone", size),
&string_struct,
|b, struct_ref| b.iter(|| black_box(struct_ref.clone())),
);
}
group.finish();
}
fn bench_nested_structs(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("nested_structs_3_levels_by_len");
for size in sizes {
let s = make_string(size);
let lc_nested = NestedLevel3Lc::new(&s);
let string_nested = NestedLevel3String::new(&s);
group.bench_with_input(
BenchmarkId::new("arc_nested_lc", size),
&lc_nested,
|b, struct_ref| b.iter(|| black_box(struct_ref.light_clone())),
);
group.bench_with_input(
BenchmarkId::new("string_nested_clone", size),
&string_nested,
|b, struct_ref| b.iter(|| black_box(struct_ref.clone())),
);
}
group.finish();
}
fn bench_deep_nested_by_depth(c: &mut Criterion) {
let depths = [1, 5, 10, 25, 50, 100];
let string_size = 1000; let s = make_string(string_size);
let mut group = c.benchmark_group("deep_nested_by_depth");
for depth in depths {
let lc_nested = DeepNestedLc::new(depth, &s);
let string_nested = DeepNestedString::new(depth, &s);
group.bench_with_input(
BenchmarkId::new("arc_lc", depth),
&lc_nested,
|b, struct_ref| b.iter(|| black_box(struct_ref.light_clone())),
);
group.bench_with_input(
BenchmarkId::new("string_clone", depth),
&string_nested,
|b, struct_ref| b.iter(|| black_box(struct_ref.clone())),
);
}
group.finish();
}
fn bench_deep_nested_by_size(c: &mut Criterion) {
let depth = 50;
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("deep_nested_50_levels_by_size");
for size in sizes {
let s = make_string(size);
let lc_nested = DeepNestedLc::new(depth, &s);
let string_nested = DeepNestedString::new(depth, &s);
group.bench_with_input(
BenchmarkId::new("arc_lc", size),
&lc_nested,
|b, struct_ref| b.iter(|| black_box(struct_ref.light_clone())),
);
group.bench_with_input(
BenchmarkId::new("string_clone", size),
&string_nested,
|b, struct_ref| b.iter(|| black_box(struct_ref.clone())),
);
}
group.finish();
}
fn bench_string_sizes(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("string_size_comparison_by_len");
for size in sizes {
let s = make_string(size);
let arc_s: Arc<str> = Arc::from(s.as_str());
let string_s = s.clone();
group.bench_with_input(BenchmarkId::new("arc_str_lc", size), &arc_s, |b, arc| {
b.iter(|| black_box(arc.light_clone()))
});
group.bench_with_input(
BenchmarkId::new("string_clone", size),
&string_s,
|b, string| b.iter(|| black_box(string.clone())),
);
}
group.finish();
}
fn bench_arc_lc_vs_clone(c: &mut Criterion) {
let arc: Arc<str> = Arc::from("test string for direct comparison");
let mut group = c.benchmark_group("arc_lc_vs_clone");
group.bench_function("arc.light_clone()", |b| {
b.iter(|| black_box(&arc).light_clone())
});
group.bench_function("arc.clone()", |b| b.iter(|| black_box(&arc).clone()));
group.finish();
}
fn bench_string_clone_vs_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let append_str = "appended";
let mut group = c.benchmark_group("string__clone_vs_mutate_by_len");
for size in sizes {
let base_string = make_string(size);
group.bench_with_input(
BenchmarkId::new("string_mutate", size),
&base_string,
|b, s| {
b.iter_batched(
|| s.clone(),
|mut owned| {
owned.push_str(append_str);
black_box(owned)
},
criterion::BatchSize::SmallInput,
)
},
);
let arc_str: Arc<str> = Arc::from(base_string.as_str());
group.bench_with_input(
BenchmarkId::new("arc_str_rebuild", size),
&arc_str,
|b, arc| {
b.iter(|| {
let mut new_string = arc.as_ref().to_string();
new_string.push_str(append_str);
let new_arc: Arc<str> = Arc::from(new_string);
black_box(new_arc)
})
},
);
}
group.finish();
}
fn bench_vec_clone_vs_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("vec__clone_vs_mutate_by_len");
for size in sizes {
let vec: Vec<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("vec_mutate", size), &vec, |b, v| {
b.iter_batched(
|| v.clone(),
|mut owned| {
owned.push(999);
black_box(owned)
},
criterion::BatchSize::SmallInput,
)
});
group.bench_with_input(
BenchmarkId::new("vec_clone_then_mutate", size),
&vec,
|b, v| {
b.iter(|| {
let mut cloned = v.clone();
cloned.push(999);
black_box(cloned)
})
},
);
}
group.finish();
}
#[derive(Clone)]
#[allow(dead_code)]
struct MutableNestedStruct {
name: String,
count: i64,
data: String,
}
impl MutableNestedStruct {
fn new(s: &str) -> Self {
Self {
name: s.to_string(),
count: 42,
data: s.to_string(),
}
}
}
fn bench_struct_clone_vs_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("struct__clone_vs_mutate_by_len");
for size in sizes {
let s = make_string(size);
let mutable_struct = MutableNestedStruct::new(&s);
group.bench_with_input(
BenchmarkId::new("struct_mutate", size),
&mutable_struct,
|b, st| {
b.iter_batched(
|| st.clone(),
|mut owned| {
owned.count = 100;
black_box(owned)
},
criterion::BatchSize::SmallInput,
)
},
);
let lc_struct = NestedLevel3Lc::new(&s);
group.bench_with_input(
BenchmarkId::new("lc_struct_clone", size),
&lc_struct,
|b, st| b.iter(|| black_box(st.light_clone())),
);
}
group.finish();
}
fn bench_hashmap_clone_vs_mutate(c: &mut Criterion) {
use std::collections::HashMap;
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("hashmap__clone_vs_mutate_by_len");
for size in sizes {
let map: HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(BenchmarkId::new("hashmap_mutate", size), &map, |b, m| {
b.iter_batched(
|| m.clone(),
|mut owned| {
owned.insert(99999, 99999);
black_box(owned)
},
criterion::BatchSize::SmallInput,
)
});
group.bench_with_input(
BenchmarkId::new("hashmap_clone_then_mutate", size),
&map,
|b, m| {
b.iter(|| {
let mut cloned = m.clone();
cloned.insert(99999, 99999);
black_box(cloned)
})
},
);
}
group.finish();
}
#[cfg(any(feature = "im", feature = "imbl", feature = "rpds"))]
mod persistent_benchmarks {
use super::*;
use std::collections::HashMap;
pub fn bench_collection_clone(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("collection__clone_by_len");
for size in sizes {
let vec: Vec<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("std_vec", size), &vec, |b, v| {
b.iter(|| black_box(v.clone()))
});
#[cfg(feature = "im")]
{
let im_vec: im::Vector<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("im_vector", size), &im_vec, |b, v| {
b.iter(|| black_box(v.light_clone()))
});
}
#[cfg(feature = "imbl")]
{
let imbl_vec: imbl::Vector<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("imbl_vector", size), &imbl_vec, |b, v| {
b.iter(|| black_box(v.light_clone()))
});
}
#[cfg(feature = "rpds")]
{
let rpds_vec: rpds::Vector<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("rpds_vector", size), &rpds_vec, |b, v| {
b.iter(|| black_box(v.light_clone()))
});
}
}
group.finish();
}
pub fn bench_collection_clone_then_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("collection__clone_then_mutate_by_len");
for size in sizes {
let vec: Vec<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("std_vec", size), &vec, |b, v| {
b.iter(|| {
let mut cloned = black_box(v).clone();
cloned.push(999);
black_box(cloned)
})
});
#[cfg(feature = "im")]
{
let im_vec: im::Vector<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("im_vector", size), &im_vec, |b, v| {
b.iter(|| {
let mut cloned = black_box(v).light_clone();
cloned.push_back(999);
black_box(cloned)
})
});
}
#[cfg(feature = "imbl")]
{
let imbl_vec: imbl::Vector<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("imbl_vector", size), &imbl_vec, |b, v| {
b.iter(|| {
let mut cloned = black_box(v).light_clone();
cloned.push_back(999);
black_box(cloned)
})
});
}
#[cfg(feature = "rpds")]
{
let rpds_vec: rpds::Vector<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("rpds_vector", size), &rpds_vec, |b, v| {
b.iter(|| {
let cloned = black_box(v).light_clone().push_back(999);
black_box(cloned)
})
});
}
}
group.finish();
}
pub fn bench_map_clone(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("map__clone_by_len");
for size in sizes {
let hash_map: HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("std_hashmap", size),
&hash_map,
|b, map| b.iter(|| black_box(map.clone())),
);
#[cfg(feature = "im")]
{
let im_map: im::HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(BenchmarkId::new("im_hashmap", size), &im_map, |b, map| {
b.iter(|| black_box(map.light_clone()))
});
}
#[cfg(feature = "imbl")]
{
let imbl_map: imbl::HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("imbl_hashmap", size),
&imbl_map,
|b, map| b.iter(|| black_box(map.light_clone())),
);
}
#[cfg(feature = "rpds")]
{
let rpds_map: rpds::HashTrieMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("rpds_hashtriemap", size),
&rpds_map,
|b, map| b.iter(|| black_box(map.light_clone())),
);
}
}
group.finish();
}
pub fn bench_map_clone_then_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("map__clone_then_mutate_by_len");
for size in sizes {
let hash_map: HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("std_hashmap", size),
&hash_map,
|b, map| {
b.iter(|| {
let mut cloned = black_box(map).clone();
cloned.insert(99999, 99999);
black_box(cloned)
})
},
);
#[cfg(feature = "im")]
{
let im_map: im::HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(BenchmarkId::new("im_hashmap", size), &im_map, |b, map| {
b.iter(|| {
let mut cloned = black_box(map).light_clone();
cloned.insert(99999, 99999);
black_box(cloned)
})
});
}
#[cfg(feature = "imbl")]
{
let imbl_map: imbl::HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("imbl_hashmap", size),
&imbl_map,
|b, map| {
b.iter(|| {
let mut cloned = black_box(map).light_clone();
cloned.insert(99999, 99999);
black_box(cloned)
})
},
);
}
#[cfg(feature = "rpds")]
{
let rpds_map: rpds::HashTrieMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("rpds_hashtriemap", size),
&rpds_map,
|b, map| {
b.iter(|| {
let cloned = black_box(map).light_clone().insert(99999, 99999);
black_box(cloned)
})
},
);
}
}
group.finish();
}
pub fn bench_persistent_vec_clone_vs_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("persistent_vec__clone_vs_mutate_by_len");
for size in sizes {
let vec: Vec<i32> = (0..size).collect();
group.bench_with_input(BenchmarkId::new("vec_mutate_only", size), &vec, |b, v| {
b.iter_batched(
|| v.clone(),
|mut owned| {
owned.push(999);
black_box(owned)
},
criterion::BatchSize::SmallInput,
)
});
#[cfg(feature = "im")]
{
let im_vec: im::Vector<i32> = (0..size).collect();
group.bench_with_input(
BenchmarkId::new("im_vector_clone_mutate", size),
&im_vec,
|b, v| {
b.iter(|| {
let mut cloned = v.light_clone();
cloned.push_back(999);
black_box(cloned)
})
},
);
}
#[cfg(feature = "imbl")]
{
let imbl_vec: imbl::Vector<i32> = (0..size).collect();
group.bench_with_input(
BenchmarkId::new("imbl_vector_clone_mutate", size),
&imbl_vec,
|b, v| {
b.iter(|| {
let mut cloned = v.light_clone();
cloned.push_back(999);
black_box(cloned)
})
},
);
}
#[cfg(feature = "rpds")]
{
let rpds_vec: rpds::Vector<i32> = (0..size).collect();
group.bench_with_input(
BenchmarkId::new("rpds_vector_clone_mutate", size),
&rpds_vec,
|b, v| {
b.iter(|| {
let cloned = v.light_clone().push_back(999);
black_box(cloned)
})
},
);
}
}
group.finish();
}
pub fn bench_persistent_map_clone_vs_mutate(c: &mut Criterion) {
let sizes = [10, 100, 1_000, 10_000];
let mut group = c.benchmark_group("persistent_map__clone_vs_mutate_by_len");
for size in sizes {
let map: HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("hashmap_mutate_only", size),
&map,
|b, m| {
b.iter_batched(
|| m.clone(),
|mut owned| {
owned.insert(99999, 99999);
black_box(owned)
},
criterion::BatchSize::SmallInput,
)
},
);
#[cfg(feature = "im")]
{
let im_map: im::HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("im_hashmap_clone_mutate", size),
&im_map,
|b, m| {
b.iter(|| {
let mut cloned = m.light_clone();
cloned.insert(99999, 99999);
black_box(cloned)
})
},
);
}
#[cfg(feature = "imbl")]
{
let imbl_map: imbl::HashMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("imbl_hashmap_clone_mutate", size),
&imbl_map,
|b, m| {
b.iter(|| {
let mut cloned = m.light_clone();
cloned.insert(99999, 99999);
black_box(cloned)
})
},
);
}
#[cfg(feature = "rpds")]
{
let rpds_map: rpds::HashTrieMap<i32, i32> = (0..size).map(|i| (i, i * 2)).collect();
group.bench_with_input(
BenchmarkId::new("rpds_hashtriemap_clone_mutate", size),
&rpds_map,
|b, m| {
b.iter(|| {
let cloned = m.light_clone().insert(99999, 99999);
black_box(cloned)
})
},
);
}
}
group.finish();
}
}
#[cfg(not(any(feature = "im", feature = "imbl", feature = "rpds")))]
criterion_group!(
benches,
bench_arc_str,
bench_arc_large_struct,
bench_five_arc_fields,
bench_lc_vs_string_fields,
bench_nested_structs,
bench_deep_nested_by_depth,
bench_deep_nested_by_size,
bench_string_sizes,
bench_arc_lc_vs_clone,
bench_string_clone_vs_mutate,
bench_vec_clone_vs_mutate,
bench_struct_clone_vs_mutate,
bench_hashmap_clone_vs_mutate,
);
#[cfg(any(feature = "im", feature = "imbl", feature = "rpds"))]
criterion_group!(
benches,
bench_arc_str,
bench_arc_large_struct,
bench_five_arc_fields,
bench_lc_vs_string_fields,
bench_nested_structs,
bench_deep_nested_by_depth,
bench_deep_nested_by_size,
bench_string_sizes,
bench_arc_lc_vs_clone,
bench_string_clone_vs_mutate,
bench_vec_clone_vs_mutate,
bench_struct_clone_vs_mutate,
bench_hashmap_clone_vs_mutate,
persistent_benchmarks::bench_collection_clone,
persistent_benchmarks::bench_collection_clone_then_mutate,
persistent_benchmarks::bench_map_clone,
persistent_benchmarks::bench_map_clone_then_mutate,
persistent_benchmarks::bench_persistent_vec_clone_vs_mutate,
persistent_benchmarks::bench_persistent_map_clone_vs_mutate,
);
criterion_main!(benches);