mirror of
https://github.com/microsoft/edit.git
synced 2025-07-03 06:23:21 +00:00
Add buffer benchmarks (#403)
Also includes: * criterion update to v0.6 * Simplify range parameter for `GapBuffer::extract_raw` * Allow deleting multiple units via `TextBuffer::delete` * And some additional smaller improvements
This commit is contained in:
parent
f07d3c3eb4
commit
a3d9aac8f0
7 changed files with 251 additions and 44 deletions
143
Cargo.lock
generated
143
Cargo.lock
generated
|
@ -29,6 +29,12 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
|
@ -41,6 +47,17 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -101,25 +118,22 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
|||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||
checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"is-terminal",
|
||||
"itertools",
|
||||
"itertools 0.13.0",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
|
@ -132,7 +146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -172,8 +186,11 @@ version = "1.1.0"
|
|||
dependencies = [
|
||||
"criterion",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"windows-sys",
|
||||
"winresource",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -188,6 +205,18 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.6.0"
|
||||
|
@ -204,12 +233,6 @@ version = "0.15.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.9.0"
|
||||
|
@ -220,17 +243,6 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
|
@ -240,12 +252,31 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
|
@ -295,6 +326,12 @@ version = "11.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.7"
|
||||
|
@ -341,6 +378,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
|
@ -452,6 +495,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
|
@ -536,6 +585,15 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
|
@ -704,3 +762,40 @@ dependencies = [
|
|||
"toml",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
|
|
@ -54,4 +54,7 @@ features = [
|
|||
]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
criterion = { version = "0.6", features = ["html_reports"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
zstd = { version = "0.13", default-features = false }
|
||||
|
|
5
assets/editing-traces/README.md
Normal file
5
assets/editing-traces/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# editing-traces
|
||||
|
||||
This directory contains Seph Gentle's ASCII-only `rustcode` editing traces from: https://github.com/josephg/editing-traces
|
||||
|
||||
The trace was provided under the [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) license.
|
BIN
assets/editing-traces/rustcode.json.zst
Normal file
BIN
assets/editing-traces/rustcode.json.zst
Normal file
Binary file not shown.
102
benches/lib.rs
102
benches/lib.rs
|
@ -2,12 +2,109 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
use std::hint::black_box;
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
|
||||
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
|
||||
use edit::helpers::*;
|
||||
use edit::simd::MemsetSafe;
|
||||
use edit::{hash, oklab, simd, unicode};
|
||||
use edit::{arena, buffer, hash, oklab, simd, unicode};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditingTracePatch(pub usize, pub usize, pub String);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditingTraceTransaction {
|
||||
pub patches: Vec<EditingTracePatch>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditingTraceData {
|
||||
#[serde(rename = "startContent")]
|
||||
pub start_content: String,
|
||||
#[serde(rename = "endContent")]
|
||||
pub end_content: String,
|
||||
pub txns: Vec<EditingTraceTransaction>,
|
||||
}
|
||||
|
||||
fn bench_buffer(c: &mut Criterion) {
|
||||
let data = include_bytes!("../assets/editing-traces/rustcode.json.zst");
|
||||
let data = zstd::decode_all(Cursor::new(data)).unwrap();
|
||||
let data: EditingTraceData = serde_json::from_slice(&data).unwrap();
|
||||
let mut patches_with_coords = Vec::new();
|
||||
|
||||
{
|
||||
let mut tb = buffer::TextBuffer::new(false).unwrap();
|
||||
tb.set_crlf(false);
|
||||
tb.write(data.start_content.as_bytes(), true);
|
||||
|
||||
for t in &data.txns {
|
||||
for p in &t.patches {
|
||||
tb.cursor_move_to_offset(p.0);
|
||||
let beg = tb.cursor_logical_pos();
|
||||
|
||||
tb.delete(buffer::CursorMovement::Grapheme, p.1 as CoordType);
|
||||
|
||||
tb.write(p.2.as_bytes(), true);
|
||||
patches_with_coords.push((beg, p.1 as CoordType, p.2.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut actual = String::new();
|
||||
tb.save_as_string(&mut actual);
|
||||
assert_eq!(actual, data.end_content);
|
||||
}
|
||||
|
||||
let bench_gap_buffer = || {
|
||||
let mut buf = buffer::GapBuffer::new(false).unwrap();
|
||||
buf.replace(0..usize::MAX, data.start_content.as_bytes());
|
||||
|
||||
for t in &data.txns {
|
||||
for p in &t.patches {
|
||||
buf.replace(p.0..p.0 + p.1, p.2.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
buf
|
||||
};
|
||||
|
||||
let bench_text_buffer = || {
|
||||
let mut tb = buffer::TextBuffer::new(false).unwrap();
|
||||
tb.set_crlf(false);
|
||||
tb.write(data.start_content.as_bytes(), true);
|
||||
|
||||
for p in &patches_with_coords {
|
||||
tb.cursor_move_to_logical(p.0);
|
||||
tb.delete(buffer::CursorMovement::Grapheme, p.1);
|
||||
tb.write(p.2.as_bytes(), true);
|
||||
}
|
||||
|
||||
tb
|
||||
};
|
||||
|
||||
// Sanity check: If this fails, the implementation is incorrect.
|
||||
{
|
||||
let buf = bench_gap_buffer();
|
||||
let mut actual = Vec::new();
|
||||
buf.extract_raw(0..usize::MAX, &mut actual, 0);
|
||||
assert_eq!(actual, data.end_content.as_bytes());
|
||||
}
|
||||
{
|
||||
let mut tb = bench_text_buffer();
|
||||
let mut actual = String::new();
|
||||
tb.save_as_string(&mut actual);
|
||||
assert_eq!(actual, data.end_content);
|
||||
}
|
||||
|
||||
c.benchmark_group("buffer")
|
||||
.bench_function(BenchmarkId::new("GapBuffer", "rustcode"), |b| {
|
||||
b.iter(bench_gap_buffer);
|
||||
})
|
||||
.bench_function(BenchmarkId::new("TextBuffer", "rustcode"), |b| {
|
||||
b.iter(bench_text_buffer);
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_hash(c: &mut Criterion) {
|
||||
c.benchmark_group("hash")
|
||||
|
@ -104,6 +201,9 @@ fn bench_unicode(c: &mut Criterion) {
|
|||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
arena::init(128 * MEBI).unwrap();
|
||||
|
||||
bench_buffer(c);
|
||||
bench_hash(c);
|
||||
bench_oklab(c);
|
||||
bench_simd_memchr2(c);
|
||||
|
|
|
@ -245,17 +245,9 @@ impl GapBuffer {
|
|||
self.text_length = 0;
|
||||
}
|
||||
|
||||
pub fn extract_raw(
|
||||
&self,
|
||||
mut beg: usize,
|
||||
mut end: usize,
|
||||
out: &mut Vec<u8>,
|
||||
mut out_off: usize,
|
||||
) {
|
||||
debug_assert!(beg <= end && end <= self.text_length);
|
||||
|
||||
end = end.min(self.text_length);
|
||||
beg = beg.min(end);
|
||||
pub fn extract_raw(&self, range: Range<usize>, out: &mut Vec<u8>, mut out_off: usize) {
|
||||
let end = range.end.min(self.text_length);
|
||||
let mut beg = range.start.min(end);
|
||||
out_off = out_off.min(out.len());
|
||||
|
||||
if beg >= end {
|
||||
|
|
|
@ -34,7 +34,7 @@ use std::ops::Range;
|
|||
use std::rc::Rc;
|
||||
use std::str;
|
||||
|
||||
use gap_buffer::GapBuffer;
|
||||
pub use gap_buffer::GapBuffer;
|
||||
|
||||
use crate::arena::{ArenaString, scratch_arena};
|
||||
use crate::cell::SemiRefCell;
|
||||
|
@ -314,6 +314,11 @@ impl TextBuffer {
|
|||
self.newlines_are_crlf
|
||||
}
|
||||
|
||||
/// Changes the newline type without normalizing the document.
|
||||
pub fn set_crlf(&mut self, crlf: bool) {
|
||||
self.newlines_are_crlf = crlf;
|
||||
}
|
||||
|
||||
/// Changes the newline type used in the document.
|
||||
///
|
||||
/// NOTE: Cannot be undone.
|
||||
|
@ -920,6 +925,11 @@ impl TextBuffer {
|
|||
self.selection_generation
|
||||
}
|
||||
|
||||
/// Moves the cursor by `offset` and updates the selection to contain it.
|
||||
pub fn selection_update_offset(&mut self, offset: usize) {
|
||||
self.set_cursor_for_selection(self.cursor_move_to_offset_internal(self.cursor, offset));
|
||||
}
|
||||
|
||||
/// Moves the cursor to `visual_pos` and updates the selection to contain it.
|
||||
pub fn selection_update_visual(&mut self, visual_pos: Point) {
|
||||
self.set_cursor_for_selection(self.cursor_move_to_visual_internal(self.cursor, visual_pos));
|
||||
|
@ -1935,7 +1945,9 @@ impl TextBuffer {
|
|||
/// The selection is cleared after the call.
|
||||
/// Deletes characters from the buffer based on a delta from the cursor.
|
||||
pub fn delete(&mut self, granularity: CursorMovement, delta: CoordType) {
|
||||
debug_assert!(delta == -1 || delta == 1);
|
||||
if delta == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut beg;
|
||||
let mut end;
|
||||
|
@ -1943,8 +1955,8 @@ impl TextBuffer {
|
|||
if let Some(r) = self.selection_range_internal(false) {
|
||||
(beg, end) = r;
|
||||
} else {
|
||||
if (delta == -1 && self.cursor.offset == 0)
|
||||
|| (delta == 1 && self.cursor.offset >= self.text_length())
|
||||
if (delta < 0 && self.cursor.offset == 0)
|
||||
|| (delta > 0 && self.cursor.offset >= self.text_length())
|
||||
{
|
||||
// Nothing to delete.
|
||||
return;
|
||||
|
@ -2014,7 +2026,7 @@ impl TextBuffer {
|
|||
let end = self.cursor_move_to_logical_internal(beg, Point { x: CoordType::MAX, y: end.y });
|
||||
|
||||
let mut replacement = Vec::new();
|
||||
self.buffer.extract_raw(beg.offset, end.offset, &mut replacement, 0);
|
||||
self.buffer.extract_raw(beg.offset..end.offset, &mut replacement, 0);
|
||||
|
||||
let initial_len = replacement.len();
|
||||
let mut offset = 0;
|
||||
|
@ -2078,7 +2090,7 @@ impl TextBuffer {
|
|||
};
|
||||
|
||||
let mut out = Vec::new();
|
||||
self.buffer.extract_raw(beg.offset, end.offset, &mut out, 0);
|
||||
self.buffer.extract_raw(beg.offset..end.offset, &mut out, 0);
|
||||
|
||||
if delete && !out.is_empty() {
|
||||
self.edit_begin(HistoryType::Delete, beg);
|
||||
|
@ -2225,7 +2237,7 @@ impl TextBuffer {
|
|||
|
||||
// Copy the deleted portion into the undo entry.
|
||||
let deleted = &mut undo.deleted;
|
||||
self.buffer.extract_raw(off, to.offset, deleted, out_off);
|
||||
self.buffer.extract_raw(off..to.offset, deleted, out_off);
|
||||
|
||||
// Delete the portion from the buffer by enlarging the gap.
|
||||
let count = to.offset - off;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue