Skip to content

Fix several small performance inefficiencies in hot paths - #61275

Merged
miguelraz merged 5 commits into
zed-industries:mainfrom
miguelraz:small-perf-wins
Jul 19, 2026
Merged

Fix several small performance inefficiencies in hot paths#61275
miguelraz merged 5 commits into
zed-industries:mainfrom
miguelraz:small-perf-wins

Conversation

@miguelraz

@miguelraz miguelraz commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

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<Option<ScanJob>> 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:

  • I've reviewed my own diff for quality, security, and reliability
  • Unsafe blocks (if any) have justifying comments
  • The content adheres to Zed's UI standards (UX/UI and icon guidelines)
  • Tests cover the new/changed behavior
  • 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.

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Jul 19, 2026
@zed-community-bot zed-community-bot Bot added the staff Pull requests authored by a current member of Zed staff label Jul 19, 2026
summary_for_anchor::<usize> computes offset_for_anchor(anchor) and then
calls text_summary_for_range(0..offset), which walks the rope again only
to recompute the exact usize it was given — roughly four wasted O(log n)
tree walks per conversion. Call offset_for_anchor directly in
ToOffset for Anchor and FromAnchor for usize.

This is the hottest anchor-resolution path; it runs on every selection
resolution and countless render-path call sites. Other dimensions
(PointUtf16, etc.) keep summary_for_anchor, since only the byte-offset
dimension is redundant.
SharedString is backed by SmolStr, and SmolStr::from(String) copies into
a fresh Arc<str> for strings beyond the inline limit, so
line.clone().into() performed two allocations and copies per line. The
line buffer is kept and clear()ed for reuse anyway; the clone existed
only to feed the conversion. line.as_str().into() does one copy, with
inline storage for short lines.

This runs for every visible line on every frame, on the hottest
text-layout path.
Lamport timestamps (value, replica_id) uniquely identify operations;
duplicates are the same op and are deduped immediately after the sort.
Stability therefore buys nothing, and sort_unstable_by_key avoids the
allocation and extra work of a stable merge sort.
sort_*_by_key re-invokes the key function during comparisons, so each
comparison cloned a PathKey (an Arc<RelPath> refcount bump). Sort with
an explicit comparator instead.
Deferring a directory called new_jobs.remove(job_ix), shifting the tail
of the vec on every deferred dir — quadratic within a single directory.
new_jobs is already Vec<Option<ScanJob>> (a None slot is pushed for
recursive symlinks) and is consumed with .flatten(), so setting the slot
to None achieves the same result in O(1).
@miguelraz
miguelraz added this pull request to the merge queue Jul 19, 2026
Merged via the queue into zed-industries:main with commit ac5538b Jul 19, 2026
38 checks passed
@miguelraz
miguelraz deleted the small-perf-wins branch July 19, 2026 05:39
jolutz pushed a commit to jolutz/zed that referenced this pull request Aug 8, 2026
…ries#61275)

# 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<Option<ScanJob>>` 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.
playdohface pushed a commit to playdohface/zed that referenced this pull request Aug 29, 2026
…ries#61275)

# 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<Option<ScanJob>>` 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement staff Pull requests authored by a current member of Zed staff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants