## Context While looking into: #32051 and #16120 with instruments, I noticed that `TabSnapshot::to_tab_point` and `TabSnapshot::to_fold_point` are a common bottleneck between the two issues. This PR takes the first steps into closing the stated issues by improving the performance of both those functions. ### Method `to_tab_point` and `to_fold_point` iterate through each character in their rows to find tab characters and translate those characters into their respective transformations. This PR changes this iteration to take advantage of the tab character bitmap in the `Rope` data structure and goes directly to each tab character when iterating. The tab bitmap is now passed from each layer in-between the `Rope` to the `TabMap`. ### Testing I added several randomized tests to ensure that the new `to_tab_point` and `to_fold_point` functions have the same behavior as the old methods they're replacing. I also added `test_random_chunk_bitmap` on each layer the tab bitmap is passed up to the `TabMap` to make sure that the bitmap being passed is transformed correctly between the layers of `DisplayMap`. `test_random_chunk_bitmap` was added to these layers: - buffer - multi buffer - custom_highlights - inlay_map - fold_map ## Benchmarking I setup benchmarks with criterion that is runnable via `cargo bench -p editor --profile=release-fast`. When benchmarking I had my laptop plugged in and did so from the terminal with a minimal amount of processes running. I'm also on a m4 max ### Results #### To Tab Point Went from completing 6.8M iterations in 5s with an average time of `736.13 ns` to `683.38 ns` which is a `-7.1875%` improvement #### To Fold Point Went from completing 6.8M iterations in 5s with an average time of `736.55 ns` to `682.40 ns` which is a `-7.1659%` improvement #### Editor render Went from having an average render time of `62.561 µs` to `57.216 µs` which is a `-8.8248%` improvement #### Build Buffer with one long line Went from having an average buffer build time of `3.2549 ms` to `3.2635 ms` which is a `+0.2151%` regression within the margin of error #### Editor with 1000 multi cursor input Went from having an average edit time of `133.05 ms` to `122.96 ms` which is a `-7.5776%` improvement Release Notes: - N/A --------- Co-authored-by: Remco Smits <djsmits12@gmail.com> Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
103 lines
3.4 KiB
Rust
103 lines
3.4 KiB
Rust
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
|
use editor::MultiBuffer;
|
|
use gpui::TestDispatcher;
|
|
use itertools::Itertools;
|
|
use rand::{Rng, SeedableRng, rngs::StdRng};
|
|
use std::num::NonZeroU32;
|
|
use text::Bias;
|
|
use util::RandomCharIter;
|
|
|
|
fn to_tab_point_benchmark(c: &mut Criterion) {
|
|
let rng = StdRng::seed_from_u64(1);
|
|
let dispatcher = TestDispatcher::new(rng);
|
|
let cx = gpui::TestAppContext::build(dispatcher, None);
|
|
|
|
let create_tab_map = |length: usize| {
|
|
let mut rng = StdRng::seed_from_u64(1);
|
|
let text = RandomCharIter::new(&mut rng)
|
|
.take(length)
|
|
.collect::<String>();
|
|
let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
|
|
|
|
let buffer_snapshot = cx.read(|cx| buffer.read(cx).snapshot(cx));
|
|
use editor::display_map::*;
|
|
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
|
|
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
|
|
let fold_point = fold_snapshot.to_fold_point(
|
|
inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))),
|
|
Bias::Left,
|
|
);
|
|
let (_, snapshot) = TabMap::new(fold_snapshot, NonZeroU32::new(4).unwrap());
|
|
|
|
(length, snapshot, fold_point)
|
|
};
|
|
|
|
let inputs = [1024].into_iter().map(create_tab_map).collect_vec();
|
|
|
|
let mut group = c.benchmark_group("To tab point");
|
|
|
|
for (batch_size, snapshot, fold_point) in inputs {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("to_tab_point", batch_size),
|
|
&snapshot,
|
|
|bench, snapshot| {
|
|
bench.iter(|| {
|
|
snapshot.to_tab_point(fold_point);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn to_fold_point_benchmark(c: &mut Criterion) {
|
|
let rng = StdRng::seed_from_u64(1);
|
|
let dispatcher = TestDispatcher::new(rng);
|
|
let cx = gpui::TestAppContext::build(dispatcher, None);
|
|
|
|
let create_tab_map = |length: usize| {
|
|
let mut rng = StdRng::seed_from_u64(1);
|
|
let text = RandomCharIter::new(&mut rng)
|
|
.take(length)
|
|
.collect::<String>();
|
|
let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
|
|
|
|
let buffer_snapshot = cx.read(|cx| buffer.read(cx).snapshot(cx));
|
|
use editor::display_map::*;
|
|
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
|
|
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
|
|
|
|
let fold_point = fold_snapshot.to_fold_point(
|
|
inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))),
|
|
Bias::Left,
|
|
);
|
|
|
|
let (_, snapshot) = TabMap::new(fold_snapshot, NonZeroU32::new(4).unwrap());
|
|
let tab_point = snapshot.to_tab_point(fold_point);
|
|
|
|
(length, snapshot, tab_point)
|
|
};
|
|
|
|
let inputs = [1024].into_iter().map(create_tab_map).collect_vec();
|
|
|
|
let mut group = c.benchmark_group("To fold point");
|
|
|
|
for (batch_size, snapshot, tab_point) in inputs {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("to_fold_point", batch_size),
|
|
&snapshot,
|
|
|bench, snapshot| {
|
|
bench.iter(|| {
|
|
snapshot.to_fold_point(tab_point, Bias::Left);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, to_tab_point_benchmark, to_fold_point_benchmark);
|
|
criterion_main!(benches);
|