mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
Improve performance of create_highlight_endpoints
This commit is contained in:
parent
b7b1d1a2c7
commit
60e94218b2
3 changed files with 211 additions and 52 deletions
|
|
@ -1,10 +1,12 @@
|
|||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
use editor::MultiBuffer;
|
||||
use gpui::TestDispatcher;
|
||||
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
||||
use editor::{MultiBuffer, display_map::*};
|
||||
use gpui::{AppContext as _, HighlightStyle, Hsla, TestDispatcher, font, px};
|
||||
use itertools::Itertools;
|
||||
use multi_buffer::MultiBufferOffset;
|
||||
use project::project_settings::DiagnosticSeverity;
|
||||
use rand::{Rng, SeedableRng, rngs::StdRng};
|
||||
use std::num::NonZeroU32;
|
||||
use settings::SettingsStore;
|
||||
use std::{num::NonZeroU32, time::Duration};
|
||||
use text::Bias;
|
||||
use util::RandomCharIter;
|
||||
|
||||
|
|
@ -101,5 +103,112 @@ fn to_fold_point_benchmark(c: &mut Criterion) {
|
|||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, to_tab_point_benchmark, to_fold_point_benchmark);
|
||||
fn create_highlight_endpoints_benchmark(c: &mut Criterion) {
|
||||
const LINE_COUNT: usize = 20_000;
|
||||
const LINE_VIEW_PORT_COUNT: usize = 100;
|
||||
const HIGHLIGHTS_PER_LINE: usize = 4;
|
||||
|
||||
let dispatcher = TestDispatcher::new(1);
|
||||
let mut cx = gpui::TestAppContext::build(dispatcher, None);
|
||||
cx.update(|cx| {
|
||||
let store = SettingsStore::test(cx);
|
||||
cx.set_global(store);
|
||||
editor::init(cx);
|
||||
});
|
||||
|
||||
let mut text = String::new();
|
||||
let mut highlight_ranges = Vec::with_capacity(LINE_COUNT * HIGHLIGHTS_PER_LINE);
|
||||
for line in 0..LINE_COUNT {
|
||||
text.push_str("fn item_");
|
||||
text.push_str(&format!("{line:05}"));
|
||||
text.push_str("() { ");
|
||||
|
||||
let start = text.len();
|
||||
text.push_str("alpha_highlight");
|
||||
highlight_ranges.push(MultiBufferOffset(start)..MultiBufferOffset(text.len()));
|
||||
|
||||
text.push_str(" + ");
|
||||
let start = text.len();
|
||||
text.push_str("beta_highlight");
|
||||
highlight_ranges.push(MultiBufferOffset(start)..MultiBufferOffset(text.len()));
|
||||
|
||||
text.push_str(" + ");
|
||||
let start = text.len();
|
||||
text.push_str("gamma_highlight");
|
||||
highlight_ranges.push(MultiBufferOffset(start)..MultiBufferOffset(text.len()));
|
||||
|
||||
text.push_str(" + ");
|
||||
let start = text.len();
|
||||
text.push_str("delta_highlight");
|
||||
highlight_ranges.push(MultiBufferOffset(start)..MultiBufferOffset(text.len()));
|
||||
|
||||
text.push_str("; }\n");
|
||||
}
|
||||
|
||||
let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
|
||||
let buffer_snapshot = cx.read(|cx| buffer.read(cx).snapshot(cx));
|
||||
let highlight_ranges = highlight_ranges
|
||||
.into_iter()
|
||||
.map(|range| {
|
||||
buffer_snapshot.anchor_before(range.start)..buffer_snapshot.anchor_before(range.end)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let map = cx.new(|cx| {
|
||||
DisplayMap::new(
|
||||
buffer,
|
||||
font("Courier"),
|
||||
px(16.0),
|
||||
None,
|
||||
1,
|
||||
1,
|
||||
FoldPlaceholder::default(),
|
||||
DiagnosticSeverity::Warning,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.update(|cx| {
|
||||
map.update(cx, |map, cx| {
|
||||
map.highlight_text(
|
||||
HighlightKey::Editor,
|
||||
highlight_ranges,
|
||||
HighlightStyle {
|
||||
color: Some(Hsla::blue()),
|
||||
..Default::default()
|
||||
},
|
||||
false,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
});
|
||||
let snapshot = cx.update(|cx| map.update(cx, |map, cx| map.snapshot(cx)));
|
||||
|
||||
let mut group = c.benchmark_group("Create highlight endpoints");
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(10));
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("text_highlights", LINE_VIEW_PORT_COUNT),
|
||||
&snapshot,
|
||||
|bench, snapshot| {
|
||||
bench.iter(|| {
|
||||
black_box(snapshot.chunks(
|
||||
DisplayRow(400)..DisplayRow(400 + LINE_VIEW_PORT_COUNT as u32),
|
||||
language::LanguageAwareStyling {
|
||||
tree_sitter: false,
|
||||
diagnostics: false,
|
||||
},
|
||||
Default::default(),
|
||||
));
|
||||
});
|
||||
},
|
||||
);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
to_tab_point_benchmark,
|
||||
to_fold_point_benchmark,
|
||||
create_highlight_endpoints_benchmark
|
||||
);
|
||||
criterion_main!(benches);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use collections::BTreeMap;
|
||||
use gpui::HighlightStyle;
|
||||
use language::{Chunk, LanguageAwareStyling};
|
||||
use multi_buffer::{MultiBufferChunks, MultiBufferOffset, MultiBufferSnapshot, ToOffset as _};
|
||||
use multi_buffer::{MultiBufferChunks, MultiBufferOffset, MultiBufferSnapshot};
|
||||
use std::{
|
||||
cmp,
|
||||
iter::{self, Peekable},
|
||||
|
|
@ -81,6 +81,8 @@ fn create_highlight_endpoints(
|
|||
if let Some(text_highlights) = text_highlights {
|
||||
let start = buffer.anchor_after(range.start);
|
||||
let end = buffer.anchor_after(range.end);
|
||||
let mut v = Vec::new();
|
||||
|
||||
for (&tag, text_highlights) in text_highlights.iter() {
|
||||
let style = text_highlights.0;
|
||||
let ranges = &text_highlights.1;
|
||||
|
|
@ -94,25 +96,39 @@ fn create_highlight_endpoints(
|
|||
})
|
||||
.unwrap_or_else(|i| i);
|
||||
|
||||
highlight_endpoints.reserve(2 * end_ix);
|
||||
let ranges_ = &ranges[start_ix..][..end_ix];
|
||||
v.clear();
|
||||
v.reserve(ranges_.len());
|
||||
highlight_endpoints.reserve(2 * ranges_.len());
|
||||
|
||||
for range in &ranges[start_ix..][..end_ix] {
|
||||
let start = range.start.to_offset(buffer);
|
||||
let end = range.end.to_offset(buffer);
|
||||
if start == end {
|
||||
continue;
|
||||
}
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: start,
|
||||
tag,
|
||||
style: Some(style),
|
||||
});
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: end,
|
||||
tag,
|
||||
style: None,
|
||||
});
|
||||
}
|
||||
let mut iter = ranges_.iter();
|
||||
buffer.summaries_for_anchors_cb(
|
||||
ranges_.iter().map(|r| &r.start),
|
||||
|start: MultiBufferOffset| {
|
||||
v.push((start, iter.next().unwrap().end));
|
||||
},
|
||||
);
|
||||
v.sort_by(|a, b| a.1.cmp(&b.1, buffer));
|
||||
let mut iter = v.iter();
|
||||
buffer.summaries_for_anchors_cb(
|
||||
v.iter().map(|(_, end)| end),
|
||||
|end: MultiBufferOffset| {
|
||||
let start = iter.next().unwrap().0;
|
||||
if start == end {
|
||||
return;
|
||||
}
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: start,
|
||||
tag,
|
||||
style: Some(style),
|
||||
});
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: end,
|
||||
tag,
|
||||
style: None,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(semantic_token_highlights) = semantic_token_highlights {
|
||||
|
|
@ -133,27 +149,50 @@ fn create_highlight_endpoints(
|
|||
.then(cmp::Ordering::Less)
|
||||
})
|
||||
.unwrap_or_else(|i| i);
|
||||
for token in &semantic_token_highlights[start_ix..] {
|
||||
if token.range.start.cmp(&end, buffer).is_ge() {
|
||||
break;
|
||||
}
|
||||
let end_ix = semantic_token_highlights[start_ix..]
|
||||
.binary_search_by(|probe| {
|
||||
probe
|
||||
.range
|
||||
.start
|
||||
.cmp(&end, buffer)
|
||||
.then(cmp::Ordering::Greater)
|
||||
})
|
||||
.unwrap_or_else(|i| i);
|
||||
|
||||
let start = token.range.start.to_offset(buffer);
|
||||
let end = token.range.end.to_offset(buffer);
|
||||
if start == end {
|
||||
continue;
|
||||
}
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: start,
|
||||
tag: HighlightKey::SemanticToken,
|
||||
style: Some(interner[token.style]),
|
||||
});
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: end,
|
||||
tag: HighlightKey::SemanticToken,
|
||||
style: None,
|
||||
});
|
||||
}
|
||||
let ranges_ = &semantic_token_highlights[start_ix..][..end_ix];
|
||||
let mut ranges_with_offsets = Vec::with_capacity(ranges_.len());
|
||||
highlight_endpoints.reserve(2 * ranges_.len());
|
||||
|
||||
let mut iter = ranges_.iter();
|
||||
buffer.summaries_for_anchors_cb(
|
||||
ranges_.iter().map(|token| &token.range.start),
|
||||
|start: MultiBufferOffset| {
|
||||
ranges_with_offsets.push((start, iter.next().unwrap()));
|
||||
},
|
||||
);
|
||||
ranges_with_offsets.sort_by(|a, b| a.1.range.end.cmp(&b.1.range.end, buffer));
|
||||
let mut iter = ranges_with_offsets.iter();
|
||||
buffer.summaries_for_anchors_cb(
|
||||
ranges_with_offsets
|
||||
.iter()
|
||||
.map(|(_, token)| &token.range.end),
|
||||
|end: MultiBufferOffset| {
|
||||
let (start, token) = iter.next().unwrap();
|
||||
if *start == end {
|
||||
return;
|
||||
}
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: *start,
|
||||
tag: HighlightKey::SemanticToken,
|
||||
style: Some(interner[token.style]),
|
||||
});
|
||||
highlight_endpoints.push(HighlightEndpoint {
|
||||
offset: end,
|
||||
tag: HighlightKey::SemanticToken,
|
||||
style: None,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
highlight_endpoints.sort();
|
||||
|
|
|
|||
|
|
@ -5003,6 +5003,20 @@ impl MultiBufferSnapshot {
|
|||
}
|
||||
|
||||
pub fn summaries_for_anchors<'a, MBD, I>(&'a self, anchors: I) -> Vec<MBD>
|
||||
where
|
||||
MBD: MultiBufferDimension
|
||||
+ Ord
|
||||
+ Sub<Output = MBD::TextDimension>
|
||||
+ AddAssign<MBD::TextDimension>,
|
||||
MBD::TextDimension: Sub<Output = MBD::TextDimension> + Ord,
|
||||
I: 'a + IntoIterator<Item = &'a Anchor>,
|
||||
{
|
||||
let mut summaries = Vec::new();
|
||||
self.summaries_for_anchors_cb(anchors, |summary| summaries.push(summary));
|
||||
summaries
|
||||
}
|
||||
|
||||
pub fn summaries_for_anchors_cb<'a, MBD, I>(&'a self, anchors: I, mut cb: impl FnMut(MBD))
|
||||
where
|
||||
MBD: MultiBufferDimension
|
||||
+ Ord
|
||||
|
|
@ -5018,18 +5032,17 @@ impl MultiBufferSnapshot {
|
|||
.cursor::<Dimensions<ExcerptDimension<MBD>, OutputDimension<MBD>>>(());
|
||||
diff_transforms_cursor.next();
|
||||
|
||||
let mut summaries = Vec::new();
|
||||
while let Some(anchor) = anchors.peek() {
|
||||
let target = anchor.seek_target(self);
|
||||
let excerpt_anchor = match anchor {
|
||||
Anchor::Min => {
|
||||
summaries.push(MBD::default());
|
||||
cb(MBD::default());
|
||||
anchors.next();
|
||||
continue;
|
||||
}
|
||||
Anchor::Excerpt(excerpt_anchor) => excerpt_anchor,
|
||||
Anchor::Max => {
|
||||
summaries.push(MBD::from_summary(&self.text_summary()));
|
||||
cb(MBD::from_summary(&self.text_summary()));
|
||||
anchors.next();
|
||||
continue;
|
||||
}
|
||||
|
|
@ -5047,7 +5060,7 @@ impl MultiBufferSnapshot {
|
|||
excerpt_start_position,
|
||||
&mut diff_transforms_cursor,
|
||||
);
|
||||
summaries.push(position);
|
||||
cb(position);
|
||||
anchors.next();
|
||||
continue;
|
||||
}
|
||||
|
|
@ -5083,7 +5096,7 @@ impl MultiBufferSnapshot {
|
|||
diff_transforms_cursor.seek_forward(&position, Bias::Left);
|
||||
}
|
||||
|
||||
summaries.push(self.summary_for_anchor_with_excerpt_position(
|
||||
cb(self.summary_for_anchor_with_excerpt_position(
|
||||
excerpt_anchor,
|
||||
position,
|
||||
&mut diff_transforms_cursor,
|
||||
|
|
@ -5097,12 +5110,10 @@ impl MultiBufferSnapshot {
|
|||
excerpt_start_position,
|
||||
&mut diff_transforms_cursor,
|
||||
);
|
||||
summaries.push(position);
|
||||
cb(position);
|
||||
anchors.next();
|
||||
}
|
||||
}
|
||||
|
||||
summaries
|
||||
}
|
||||
|
||||
pub fn dimensions_from_points<'a, MBD>(
|
||||
|
|
|
|||
Loading…
Reference in a new issue