use criterion::{criterion_group, criterion_main, Criterion}; mod tree { use super::*; use criterion::{AxisScale, BenchmarkId, PlotConfiguration}; use loro_internal::{LoroDoc, TreeParentId}; use rand::{rngs::StdRng, Rng}; pub fn tree_move(c: &mut Criterion) { let mut group = c.benchmark_group("movable tree"); let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); group.plot_config(plot_config); group.sample_size(10); for i in 3..=6 { let input = 10u64.pow(i); group.bench_with_input( BenchmarkId::new("create node append", input), &input, |b, i| { b.iter(|| { let loro = LoroDoc::new_auto_commit(); let tree = loro.get_tree("tree"); for idx in 0..*i { tree.create_at(TreeParentId::Root, idx as usize).unwrap(); } }) }, ); group.bench_with_input( BenchmarkId::new("move node append", input), &input, |b, i| { let loro = LoroDoc::new_auto_commit(); let tree = loro.get_tree("tree"); const SIZE: usize = 1000; let mut rng: StdRng = rand::SeedableRng::seed_from_u64(0); let mut ids = vec![]; for _ in 0..SIZE { let pos = rng.gen::() % (ids.len() + 1); ids.push(tree.create_at(TreeParentId::Root, pos).unwrap()); } b.iter(|| { for _ in 0..*i { tree.create_at(TreeParentId::Root, 0).unwrap(); let i = rng.gen::() % SIZE; let j = rng.gen::() % SIZE; tree.mov(ids[i], TreeParentId::Node(ids[j])) .unwrap_or_default(); } }) }, ); } group.bench_function("1000 node checkout 10^3", |b| { let loro = LoroDoc::default(); loro.start_auto_commit(); let tree = loro.get_tree("tree"); let mut ids = vec![]; let mut versions = vec![]; let size = 1000; for _ in 0..size { ids.push(tree.create(TreeParentId::Root).unwrap()) } let mut rng: StdRng = rand::SeedableRng::seed_from_u64(0); let mut n = 1000; while n > 0 { let i = rng.gen::() % size; let j = rng.gen::() % size; if tree.mov(ids[i], TreeParentId::Node(ids[j])).is_ok() { versions.push(loro.oplog_frontiers()); n -= 1; }; } b.iter(|| { for _ in 0..1000 { let i = rng.gen::() % 1000; let f = &versions[i]; loro.checkout(f).unwrap(); } }) }); group.bench_function("300 deep node random checkout 10^3", |b| { let depth = 300; let loro = LoroDoc::default(); loro.start_auto_commit(); let tree = loro.get_tree("tree"); let mut ids = vec![]; let mut versions = vec![]; let id1 = tree.create(TreeParentId::Root).unwrap(); ids.push(id1); versions.push(loro.oplog_frontiers()); for _ in 1..depth { let id = tree .create(TreeParentId::Node(*ids.last().unwrap())) .unwrap(); ids.push(id); versions.push(loro.oplog_frontiers()); } let mut rng: StdRng = rand::SeedableRng::seed_from_u64(0); b.iter(|| { for _ in 0..1000 { let i = rng.gen::() % depth; let f = &versions[i]; loro.checkout(f).unwrap(); } }) }); group.bench_function("realtime tree move", |b| { let doc_a = LoroDoc::default(); let doc_b = LoroDoc::default(); doc_a.start_auto_commit(); doc_b.start_auto_commit(); let tree_a = doc_a.get_tree("tree"); let tree_b = doc_b.get_tree("tree"); let mut ids = vec![]; let size = 1000; for _ in 0..size { ids.push(tree_a.create(TreeParentId::Root).unwrap()) } doc_b.import(&doc_a.export_snapshot().unwrap()).unwrap(); let mut rng: StdRng = rand::SeedableRng::seed_from_u64(0); let n = 1000; b.iter(|| { for t in 0..n { let i = rng.gen::() % size; let j = rng.gen::() % size; if t % 2 == 0 { tree_a .mov(ids[i], TreeParentId::Node(ids[j])) .unwrap_or_default(); doc_b.import(&doc_a.export_from(&doc_b.oplog_vv())).unwrap(); } else { tree_b .mov(ids[i], TreeParentId::Node(ids[j])) .unwrap_or_default(); doc_a.import(&doc_b.export_from(&doc_a.oplog_vv())).unwrap(); } } }) }); group.finish(); } } criterion_group!(benches, tree::tree_move); criterion_main!(benches);