diff --git a/crates/loro-core/benches/text.rs b/crates/loro-core/benches/text.rs index c0a8346b..52afa986 100644 --- a/crates/loro-core/benches/text.rs +++ b/crates/loro-core/benches/text.rs @@ -15,7 +15,7 @@ mod run { use rand::SeedableRng; use serde_json::Value; - pub fn criterion_benchmark(c: &mut Criterion) { + pub fn two_client_edits(c: &mut Criterion) { let mut rgn = rand::rngs::StdRng::seed_from_u64(0); let mut bytes = Vec::new(); for _ in 0..1000 { @@ -27,6 +27,10 @@ mod run { c.bench_function("random text edit 2 sites", |b| { b.iter(|| test_multi_sites(2, actions.clone().into())) }); + + c.bench_function("random text edit 8 sites", |b| { + b.iter(|| test_multi_sites(8, actions.clone().into())) + }); } pub fn b4(c: &mut Criterion) { @@ -35,6 +39,7 @@ mod run { d.read_to_string(&mut s).unwrap(); let json: Value = serde_json::from_str(&s).unwrap(); let txns = json.as_object().unwrap().get("txns"); + println!("{}", txns.unwrap().as_array().unwrap().len()); c.bench_function("B4", |b| { b.iter(|| { let mut loro = LoroCore::default(); @@ -57,12 +62,41 @@ mod run { } }) }); + + let mut b = c.benchmark_group("sync"); + b.sample_size(10); + b.bench_function("B4Sync", |b| { + b.iter(|| { + let mut loro = LoroCore::default(); + let mut loro_b = LoroCore::default(); + for txn in txns.unwrap().as_array().unwrap() { + let mut text = loro.get_or_create_root_text("text").unwrap(); + let patches = txn + .as_object() + .unwrap() + .get("patches") + .unwrap() + .as_array() + .unwrap(); + for patch in patches { + let pos = patch[0].as_u64().unwrap() as usize; + let del_here = patch[1].as_u64().unwrap() as usize; + let ins_content = patch[2].as_str().unwrap(); + text.delete(pos, del_here); + text.insert(pos, ins_content); + } + + drop(text); + loro_b.import(loro.export(loro_b.vv())); + } + }) + }); } } pub fn dumb(_c: &mut Criterion) {} #[cfg(feature = "fuzzing")] -criterion_group!(benches, run::criterion_benchmark, run::b4); +criterion_group!(benches, run::two_client_edits, run::b4); #[cfg(not(feature = "fuzzing"))] criterion_group!(benches, dumb); criterion_main!(benches); diff --git a/crates/loro-core/examples/text_sync.rs b/crates/loro-core/examples/text_sync.rs new file mode 100644 index 00000000..72f14b22 --- /dev/null +++ b/crates/loro-core/examples/text_sync.rs @@ -0,0 +1,42 @@ +#[cfg(not(feature = "fuzzing"))] +fn main() {} + +#[cfg(feature = "fuzzing")] +fn main() { + const RAW_DATA: &[u8; 901823] = include_bytes!("../benches/automerge-paper.json.gz"); + use std::io::Read; + + use flate2::read::GzDecoder; + use loro_core::LoroCore; + use serde_json::Value; + + let mut d = GzDecoder::new(&RAW_DATA[..]); + let mut s = String::new(); + d.read_to_string(&mut s).unwrap(); + let json: Value = serde_json::from_str(&s).unwrap(); + let txns = json.as_object().unwrap().get("txns"); + println!("Txn: {}", txns.unwrap().as_array().unwrap().len()); + + let mut loro = LoroCore::default(); + let mut loro_b = LoroCore::default(); + for txn in txns.unwrap().as_array().unwrap() { + let mut text = loro.get_or_create_root_text("text").unwrap(); + let patches = txn + .as_object() + .unwrap() + .get("patches") + .unwrap() + .as_array() + .unwrap(); + for patch in patches { + let pos = patch[0].as_u64().unwrap() as usize; + let del_here = patch[1].as_u64().unwrap() as usize; + let ins_content = patch[2].as_str().unwrap(); + text.delete(pos, del_here); + text.insert(pos, ins_content); + } + + drop(text); + loro_b.import(loro.export(loro_b.vv())); + } +} diff --git a/crates/loro-core/justfile b/crates/loro-core/justfile index a8de5b87..14abc1c5 100644 --- a/crates/loro-core/justfile +++ b/crates/loro-core/justfile @@ -25,7 +25,7 @@ quick-fuzz: cargo fuzz run text -- -max_total_time=10 -max_len=1000 flame: - cargo flamegraph --example test --features=fuzzing --root + cargo flamegraph --example text_sync --features=fuzzing --root bench *FLAGS: cargo bench --features fuzzing {{FLAGS}} diff --git a/crates/loro-core/src/dag.rs b/crates/loro-core/src/dag.rs index 343e647d..a18acfdf 100644 --- a/crates/loro-core/src/dag.rs +++ b/crates/loro-core/src/dag.rs @@ -26,7 +26,7 @@ use crate::{ change::Lamport, debug_log, id::{ClientID, Counter, ID}, - span::{HasId, HasIdSpan, HasLamport, HasLamportSpan, IdSpan}, + span::{CounterSpan, HasId, HasIdSpan, HasLamport, HasLamportSpan, IdSpan}, version::{IdSpanVector, VersionVector, VersionVectorDiff}, }; @@ -111,6 +111,29 @@ impl DagUtils for T { if from == to { return ans; } + if from.len() == 1 && to.len() == 1 { + let from = from[0]; + let to = to[0]; + if from.client_id == to.client_id { + let from_span = self.get(from).unwrap(); + let to_span = self.get(to).unwrap(); + if from_span.deps().len() == 1 && to_span.contains_id(from_span.deps()[0]) { + ans.left.insert( + from.client_id, + CounterSpan::new(to.counter + 1, from.counter + 1), + ); + return ans; + } + + if to_span.deps().len() == 1 && from_span.contains_id(to_span.deps()[0]) { + ans.right.insert( + from.client_id, + CounterSpan::new(from.counter + 1, to.counter + 1), + ); + return ans; + } + } + } _find_common_ancestor( &|v| self.get(v),