diff --git a/Cargo.lock b/Cargo.lock index c9cb4099e5..e3617b8339 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.5.0" @@ -1806,6 +1812,12 @@ dependencies = [ "winx", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "castaway" version = "0.1.2" @@ -1914,6 +1926,33 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.3.0" @@ -2783,6 +2822,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap 3.2.25", + "criterion-plot", + "itertools 0.10.5", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -2836,6 +2911,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-bigint" version = "0.4.9" @@ -4478,6 +4559,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -6399,6 +6490,12 @@ dependencies = [ "zvariant", ] +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -6879,6 +6976,34 @@ dependencies = [ "time", ] +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + [[package]] name = "png" version = "0.16.8" @@ -7794,6 +7919,7 @@ version = "0.1.0" dependencies = [ "arrayvec 0.7.4", "bromberg_sl2", + "criterion", "gpui", "log", "rand 0.8.5", @@ -9831,6 +9957,16 @@ dependencies = [ "url", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/crates/rope/Cargo.toml b/crates/rope/Cargo.toml index b1ce369416..577fbf9c0d 100644 --- a/crates/rope/Cargo.toml +++ b/crates/rope/Cargo.toml @@ -23,3 +23,8 @@ util.workspace = true gpui = { workspace = true, features = ["test-support"] } rand.workspace = true util = { workspace = true, features = ["test-support"] } +criterion = { version = "0.4", features = ["html_reports"] } + +[[bench]] +name = "rope_benchmark" +harness = false diff --git a/crates/rope/benches/rope_benchmark.rs b/crates/rope/benches/rope_benchmark.rs new file mode 100644 index 0000000000..a1d04474d3 --- /dev/null +++ b/crates/rope/benches/rope_benchmark.rs @@ -0,0 +1,144 @@ +use std::ops::Range; + +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; +use rand::prelude::*; +use rand::rngs::StdRng; +use rope::Rope; +use util::RandomCharIter; + +fn generate_random_text(mut rng: StdRng, text_len: usize) -> String { + RandomCharIter::new(&mut rng).take(text_len).collect() +} + +fn generate_random_rope(rng: StdRng, text_len: usize) -> Rope { + let text = generate_random_text(rng, text_len); + let mut rope = Rope::new(); + rope.push(&text); + rope +} + +fn generate_random_rope_ranges(mut rng: StdRng, rope: &Rope) -> Vec> { + let range_max_len = 50; + let num_ranges = rope.len() / range_max_len; + + let mut ranges = Vec::new(); + let mut start = 0; + for _ in 0..num_ranges { + let range_start = rope.clip_offset( + rng.gen_range(start..=(start + range_max_len)), + sum_tree::Bias::Left, + ); + let range_end = rope.clip_offset( + rng.gen_range(range_start..(range_start + range_max_len)), + sum_tree::Bias::Right, + ); + + let range = range_start..range_end; + if !range.is_empty() { + ranges.push(range); + } + + start = range_end + 1; + } + + ranges +} + +fn rope_benchmarks(c: &mut Criterion) { + static SEED: u64 = 9999; + static KB: usize = 1024; + + let rng = StdRng::seed_from_u64(SEED); + let sizes = [4 * KB, 64 * KB]; + + let mut group = c.benchmark_group("push"); + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| { + let text = generate_random_text(rng.clone(), *size); + + b.iter(|| { + let mut rope = Rope::new(); + for _ in 0..10 { + rope.push(&text); + } + }); + }); + } + group.finish(); + + let mut group = c.benchmark_group("append"); + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| { + let mut random_ropes = Vec::new(); + for _ in 0..5 { + random_ropes.push(generate_random_rope(rng.clone(), *size)); + } + + b.iter(|| { + let mut rope_b = Rope::new(); + for rope in &random_ropes { + rope_b.append(rope.clone()) + } + }); + }); + } + group.finish(); + + let mut group = c.benchmark_group("slice"); + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| { + let rope = generate_random_rope(rng.clone(), *size); + + b.iter_batched( + || generate_random_rope_ranges(rng.clone(), &rope), + |ranges| { + for range in ranges.iter() { + rope.slice(range.clone()); + } + }, + BatchSize::SmallInput, + ); + }); + } + group.finish(); + + let mut group = c.benchmark_group("bytes_in_range"); + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| { + let rope = generate_random_rope(rng.clone(), *size); + + b.iter_batched( + || generate_random_rope_ranges(rng.clone(), &rope), + |ranges| { + for range in ranges.iter() { + let bytes = rope.bytes_in_range(range.clone()); + assert!(bytes.into_iter().count() > 0); + } + }, + BatchSize::SmallInput, + ); + }); + } + group.finish(); + + let mut group = c.benchmark_group("chars"); + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| { + let rope = generate_random_rope(rng.clone(), *size); + + b.iter_with_large_drop(|| { + let chars = rope.chars().count(); + assert!(chars > 0); + }); + }); + } + group.finish(); +} + +criterion_group!(benches, rope_benchmarks); +criterion_main!(benches); diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 77e86e3031..852910cacc 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -23,7 +23,7 @@ pub use unclipped::Unclipped; const CHUNK_BASE: usize = 6; #[cfg(not(test))] -const CHUNK_BASE: usize = 16; +const CHUNK_BASE: usize = 64; /// Type alias to [`HashMatrix`], an implementation of a homomorphic hash function. Two [`Rope`] instances /// containing the same text will produce the same fingerprint. This hash function is special in that