From d0f6c0d1d127be80b5e8fb77357a08d7c73baf6a Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 13:31:05 +0000 Subject: [PATCH] editor: More perf work (#49465) (cherry-pick to preview) (#49470) Cherry-pick of #49465 to preview ---- Seems that `SmallVec::clone` is pretty expensive in a generic case, and specialising it improves the performance quite a bit! Release Notes: - Improved performance of different building blocks within the MultiBuffer. --------- Co-authored-by: Lukas Wirth Co-authored-by: Jakub Konka Co-authored-by: Lukas Wirth --- crates/buffer_diff/src/buffer_diff.rs | 37 +++++++++---------- crates/clock/src/clock.rs | 16 ++++++++- crates/editor/src/split.rs | 6 ++-- crates/multi_buffer/src/multi_buffer.rs | 48 ++++++++++++------------- crates/multi_buffer/src/path_key.rs | 2 +- 5 files changed, 60 insertions(+), 49 deletions(-) diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index 18428a95aa2599273b1bda56f07084f67577fc3e..c34cd0e49e13e6bb16ca8a76672649e8d3772763 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -914,28 +914,25 @@ impl BufferDiffInner { buffer: &'a text::BufferSnapshot, secondary: Option<&'a Self>, ) -> impl 'a + Iterator { - let mut cursor = self.hunks.filter::<_, DiffHunkSummary>(buffer, filter); - - let anchor_iter = iter::from_fn(move || { - cursor.next(); - cursor.item() - }) - .flat_map(move |hunk| { - [ - ( - &hunk.buffer_range.start, + let anchor_iter = self + .hunks + .filter::<_, DiffHunkSummary>(buffer, filter) + .flat_map(move |hunk| { + [ ( - hunk.buffer_range.start, - hunk.diff_base_byte_range.start, - hunk, + &hunk.buffer_range.start, + ( + hunk.buffer_range.start, + hunk.diff_base_byte_range.start, + hunk, + ), ), - ), - ( - &hunk.buffer_range.end, - (hunk.buffer_range.end, hunk.diff_base_byte_range.end, hunk), - ), - ] - }); + ( + &hunk.buffer_range.end, + (hunk.buffer_range.end, hunk.diff_base_byte_range.end, hunk), + ), + ] + }); let mut pending_hunks_cursor = self.pending_hunks.cursor::(buffer); pending_hunks_cursor.next(); diff --git a/crates/clock/src/clock.rs b/crates/clock/src/clock.rs index 6526ae1d55ead3e46b5fab293258492359fa2b24..cb0808abcae1709020f3fd3077436aeb1140a140 100644 --- a/crates/clock/src/clock.rs +++ b/crates/clock/src/clock.rs @@ -66,13 +66,27 @@ pub struct Lamport { } /// A [version vector](https://en.wikipedia.org/wiki/Version_vector). -#[derive(Clone, Default, Hash, Eq, PartialEq)] +#[derive(Default, Hash, Eq, PartialEq)] pub struct Global { // 4 is chosen as it is the biggest count that does not increase the size of the field itself. // Coincidentally, it also covers all the important non-collab replica ids. values: SmallVec<[u32; 4]>, } +impl Clone for Global { + fn clone(&self) -> Self { + // We manually implement clone to avoid the overhead of SmallVec's clone implementation. + // Using `from_slice` is faster than `clone` for SmallVec as we can use our `Copy` implementation of u32. + Self { + values: SmallVec::from_slice(&self.values), + } + } + + fn clone_from(&mut self, source: &Self) { + self.values.clone_from(&source.values); + } +} + impl Global { pub fn new() -> Self { Self::default() diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index b37a95d3f4239d95a359c9e5f0c35cf5e4013a7b..18f07f169318ad1e40123caf5b9ebcc0db2cf706 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -2013,14 +2013,14 @@ impl LhsEditor { diff: Entity, lhs_cx: &mut Context, ) -> Option<(Vec, Vec>)> { - let rhs_excerpt_ids: Vec = - rhs_multibuffer.excerpts_for_path(&path_key).collect(); - let Some(excerpt_id) = rhs_multibuffer.excerpts_for_path(&path_key).next() else { lhs_multibuffer.remove_excerpts_for_path(path_key, lhs_cx); return None; }; + let rhs_excerpt_ids: Vec = + rhs_multibuffer.excerpts_for_path(&path_key).collect(); + let rhs_multibuffer_snapshot = rhs_multibuffer.snapshot(lhs_cx); let main_buffer = rhs_multibuffer_snapshot .buffer_for_excerpt(excerpt_id) diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index cd40c20499c3400feaeb041d52ddf12ede8a62ca..dfa403e7dadbfb5f6e00825af33cc1a6b194a6cc 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -10,8 +10,8 @@ pub use anchor::{Anchor, AnchorRangeExt}; use anyhow::{Result, anyhow}; use buffer_diff::{ - BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffChanged, DiffHunk, - DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind, + BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffChanged, DiffHunkSecondaryStatus, + DiffHunkStatus, DiffHunkStatusKind, }; use clock::ReplicaId; use collections::{BTreeMap, Bound, HashMap, HashSet}; @@ -2410,7 +2410,7 @@ impl MultiBuffer { diff: Entity, cx: &mut Context, ) { - let base_text_buffer_id = diff.read(cx).base_text(cx).remote_id(); + let base_text_buffer_id = diff.read(cx).base_text_buffer().read(cx).remote_id(); let diff = diff.read(cx); let diff = DiffStateSnapshot { diff: diff.snapshot(cx), @@ -2475,7 +2475,7 @@ impl MultiBuffer { self.sync_mut(cx); let diff = diff.read(cx); - let base_text_buffer_id = diff.base_text(cx).remote_id(); + let base_text_buffer_id = diff.base_text_buffer().read(cx).remote_id(); let Some(buffer_state) = self.buffers.get(&base_text_buffer_id) else { return; }; @@ -2675,8 +2675,9 @@ impl MultiBuffer { } pub fn add_inverted_diff(&mut self, diff: Entity, cx: &mut Context) { - let base_text_buffer_id = diff.read(cx).base_text(cx).remote_id(); - let diff_change_range = 0..diff.read(cx).base_text(cx).len(); + let snapshot = diff.read(cx).base_text(cx); + let base_text_buffer_id = snapshot.remote_id(); + let diff_change_range = 0..snapshot.len(); self.snapshot.get_mut().has_inverted_diff = true; self.inverted_buffer_diff_changed(diff.clone(), diff_change_range, cx); self.diffs @@ -3966,25 +3967,24 @@ impl MultiBufferSnapshot { let query_range = range.start.to_point(self)..range.end.to_point(self); self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| { let diff = self.diffs.get(&buffer.remote_id())?; - let iter: Box> = - if diff.is_inverted { - let buffer_start = buffer.point_to_offset(buffer_range.start); - let buffer_end = buffer.point_to_offset(buffer_range.end); - Box::new( - diff.hunks_intersecting_base_text_range( - buffer_start..buffer_end, - diff.original_buffer_snapshot(), - ) - .map(move |hunk| (hunk, buffer, true)), - ) - } else { - let buffer_start = buffer.anchor_before(buffer_range.start); - let buffer_end = buffer.anchor_after(buffer_range.end); - Box::new( - diff.hunks_intersecting_range(buffer_start..buffer_end, buffer) - .map(move |hunk| (hunk, buffer, false)), + let iter = if diff.is_inverted { + let buffer_start = buffer.point_to_offset(buffer_range.start); + let buffer_end = buffer.point_to_offset(buffer_range.end); + itertools::Either::Left( + diff.hunks_intersecting_base_text_range( + buffer_start..buffer_end, + diff.original_buffer_snapshot(), ) - }; + .map(move |hunk| (hunk, buffer, true)), + ) + } else { + let buffer_start = buffer.anchor_before(buffer_range.start); + let buffer_end = buffer.anchor_after(buffer_range.end); + itertools::Either::Right( + diff.hunks_intersecting_range(buffer_start..buffer_end, buffer) + .map(move |hunk| (hunk, buffer, false)), + ) + }; Some(iter.filter_map(|(hunk, buffer, is_inverted)| { if hunk.is_created_file() && !self.all_diff_hunks_expanded { return None; diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index e829ee00794bf773e30cf61f98f3013f51396f8b..453d6df97ae32a7326f0d2568b4cfbacc0dac5b6 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -57,7 +57,7 @@ impl MultiBuffer { self.excerpts_by_path .get(path) .map(|excerpts| excerpts.as_slice()) - .unwrap_or(&[]) + .unwrap_or_default() .iter() .copied() }