editor: More perf work (#49465)

Jakub Konka and Lukas Wirth created

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 <lukas@zed.dev>

Change summary

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(-)

Detailed changes

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -914,28 +914,25 @@ impl BufferDiffInner<language::BufferSnapshot> {
         buffer: &'a text::BufferSnapshot,
         secondary: Option<&'a Self>,
     ) -> impl 'a + Iterator<Item = DiffHunk> {
-        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::<DiffHunkSummary>(buffer);
         pending_hunks_cursor.next();

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()

crates/editor/src/split.rs 🔗

@@ -2013,14 +2013,14 @@ impl LhsEditor {
         diff: Entity<BufferDiff>,
         lhs_cx: &mut Context<MultiBuffer>,
     ) -> Option<(Vec<ExcerptId>, Vec<Vec<ExcerptId>>)> {
-        let rhs_excerpt_ids: Vec<ExcerptId> =
-            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<ExcerptId> =
+            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)

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};
@@ -2409,7 +2409,7 @@ impl MultiBuffer {
         diff: Entity<BufferDiff>,
         cx: &mut Context<Self>,
     ) {
-        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),
@@ -2474,7 +2474,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;
         };
@@ -2674,8 +2674,9 @@ impl MultiBuffer {
     }
 
     pub fn add_inverted_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
-        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
@@ -3965,25 +3966,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<dyn Iterator<Item = (DiffHunk, &BufferSnapshot, bool)>> =
-                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;

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()
     }