From 78652e2f507a2340a8b99816242229d69fcc5c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Sat, 18 Jul 2026 23:36:35 -0600 Subject: [PATCH] Fix several small performance inefficiencies in hot paths (#61275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective Land five small, independent performance fixes found during an audit of hot paths (anchor resolution, line shaping, worktree scanning, and sorting in multibuffers). ## Solution Each fix is its own commit, so **this PR is best reviewed commit-by-commit** — every commit message contains the full reasoning for that change: - `text`: Avoid redundant rope traversal in `Anchor → usize` conversion — call `offset_for_anchor` directly instead of `summary_for_anchor`, which recomputed the same byte offset with ~4 extra O(log n) tree walks. This is the hottest anchor-resolution path. - `editor`: Avoid double allocation per shaped line — `line.as_str().into()` instead of `line.clone().into()`, which allocated twice per visible line, every frame. - `text`: Use `sort_unstable_by_key` in operation queue insertion — Lamport timestamps are unique keys and duplicates are deduped right after, so stability buys nothing. - `multi_buffer`: Sort with an explicit comparator instead of `sort_unstable_by_key`, which cloned a `PathKey` (`Arc` refcount bump) on every comparison. - `worktree`: Replace O(n²) `Vec::remove` in the deferred-directory pass with an O(1) `None` assignment — the vec is already `Vec>` and is consumed with `.flatten()`. ## Testing - No behavior changes intended; all changes are mechanical and tests affected crates pass: `text`, `editor`, `multi_buffer`, `worktree`. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Improved editor performance through several micro-optimizations in anchor resolution, line shaping, and worktree scanning. --- crates/editor/src/element.rs | 2 +- crates/multi_buffer/src/multi_buffer.rs | 2 +- crates/text/src/operation_queue.rs | 2 +- crates/text/src/text.rs | 4 ++-- crates/worktree/src/worktree.rs | 7 +++---- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d5ca045a5d..71e9a50087 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -7099,7 +7099,7 @@ impl LineWithInvisibles { &Self::split_runs_by_bg_segments(&styles, segments, min_contrast, len) }; let shaped_line = window.text_system().shape_line( - line.clone().into(), + line.as_str().into(), font_size, text_runs, None, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index e233f393b2..9ac5396db7 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -2553,7 +2553,7 @@ impl MultiBuffer { *non_text_state_update_count += 1; } - paths_to_edit.sort_unstable_by_key(|(path, _, _, _)| path.clone()); + paths_to_edit.sort_unstable_by(|a, b| a.0.cmp(&b.0)); let mut edits = Vec::new(); let mut new_excerpts = SumTree::default(); diff --git a/crates/text/src/operation_queue.rs b/crates/text/src/operation_queue.rs index f87af381ff..21239456a3 100644 --- a/crates/text/src/operation_queue.rs +++ b/crates/text/src/operation_queue.rs @@ -47,7 +47,7 @@ impl OperationQueue { } pub fn insert(&mut self, mut ops: Vec) { - ops.sort_by_key(|op| op.lamport_timestamp()); + ops.sort_unstable_by_key(|op| op.lamport_timestamp()); ops.dedup_by_key(|op| op.lamport_timestamp()); self.0.edit( ops.into_iter() diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index b83875dbdb..e15512f3e2 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -3428,7 +3428,7 @@ impl ToOffset for usize { impl ToOffset for Anchor { #[inline] fn to_offset(&self, snapshot: &BufferSnapshot) -> usize { - snapshot.summary_for_anchor(self) + snapshot.offset_for_anchor(self) } } @@ -3570,7 +3570,7 @@ impl FromAnchor for PointUtf16 { impl FromAnchor for usize { #[inline] fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self { - snapshot.summary_for_anchor(anchor) + snapshot.offset_for_anchor(anchor) } } diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index ed06ceba7d..9ca1fa52ab 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -5402,13 +5402,12 @@ impl BackgroundScanner { for entry in &mut new_entries { state.reuse_entry_id(entry); if entry.is_dir() { - if self.should_scan_directory(&state, entry) { - job_ix += 1; - } else { + if !self.should_scan_directory(&state, entry) { log::debug!("defer scanning directory {:?}", entry.path); entry.kind = EntryKind::UnloadedDir; - new_jobs.remove(job_ix); + new_jobs[job_ix] = None; } + job_ix += 1; } if entry.is_always_included { state