Skip to content

fuzzy_nucleo: Optimize path matching with CharBag prefilter and add benchmarks - #54112

Merged
yara-blue merged 1 commit into
zed-industries:mainfrom
feitreim:feat-fuzzy-paths-optimization
Apr 17, 2026
Merged

yara-blue merged 1 commit into
zed-industries:mainfrom
feitreim:feat-fuzzy-paths-optimization

Conversation

@feitreim

@feitreim feitreim commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator

This PR was originally a part of #53551 so theres more info about its motivation there.

  • Add a CharBag prefilter on path candidates to skip irrelevant entries before invoking nucleo's matcher.
  • Use binary_search on sorted matched char indices when reconstructing byte positions (perf improvement).
  • Add a criterion benchmark comparing fuzzy_nucleo path matching against the existing fuzzy crate.

Performance Chart:

Benchmark Size Nucleo (before) Nucleo (after) Fuzzy Before/Fuzzy After/Fuzzy
1-word 100 14.14 µs 9.12 µs 9.06 µs 1.56x 1.01x
1-word 1,000 164.37 µs 114.11 µs 110.43 µs 1.49x 1.03x
1-word 10,000 1.83 ms 1.39 ms 1.41 ms 1.30x 0.99x
2-word 100 12.83 µs 3.51 µs 979 ns 13.10x 3.59x
2-word 1,000 131.65 µs 33.46 µs 6.37 µs 20.67x 5.25x
2-word 10,000 1.24 ms 338.84 µs 52.46 µs 23.64x 6.46x

Exact Current State:

query size nucleo fuzzy nucleo/fuzzy
1-word 100 8.62 µs 9.22 µs 0.93×
1-word 1000 102 µs 111 µs 0.92×
1-word 10000 1.13 ms 1.28 ms 0.88×
2-word 100 3.48 µs 0.98 µs 3.55×
2-word 1000 29.9 µs 6.39 µs 4.68×
2-word 10000 271 µs 53.4 µs 5.08×
4-word 100 0.85 µs 0.53 µs 1.60×
4-word 1000 2.99 µs 1.66 µs 1.80×
4-word 10000 20.1 µs 9.14 µs 2.20×

Self-Review Checklist:

  • I've reviewed my own diff for quality, security, and reliability
  • Unsafe blocks (if any) have justifying comments
  • The content is consistent with the UI/UX checklist
  • Tests cover the new/changed behavior
  • Performance impact has been considered and is acceptable

Release Notes:

  • fuzzy_nucleo: improved the performance of path matching

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Apr 16, 2026
@zed-codeowner-coordinator
zed-codeowner-coordinator Bot requested a review from a team April 16, 2026 18:16
@zed-community-bot zed-community-bot Bot added the guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions label Apr 16, 2026
@feitreim
feitreim force-pushed the feat-fuzzy-paths-optimization branch from 1cd815a to cb19df1 Compare April 16, 2026 19:44
@feitreim

Copy link
Copy Markdown
Collaborator Author

hey @yara-blue heres Part A from the larger PR, just the path optimizations.

@feitreim
feitreim force-pushed the feat-fuzzy-paths-optimization branch 2 times, most recently from ccdfeab to 16b790a Compare April 17, 2026 00:09
…enchmarks

- Add a CharBag prefilter on path candidates to skip irrelevant entries
  before invoking nucleo's matcher.
- Use total_cmp for score ordering and binary_search on sorted matched
  char indices when reconstructing byte positions.
- Make the internal matcher pool robust to a poisoned mutex.
- Add a criterion benchmark comparing fuzzy_nucleo path matching against
  the existing fuzzy crate.

Release Notes:

- N/A
@feitreim
feitreim force-pushed the feat-fuzzy-paths-optimization branch from 16b790a to 85c4650 Compare April 17, 2026 03:14
@feitreim

Copy link
Copy Markdown
Collaborator Author

While I was implementing the string matching into some of the existing pickers, I realized a major source of regressions was the SmartCase handling, I explain this better in #54123 but I did think it made sense to backport these changes to the path matching as well. this change is better parity with fuzzy, but at a slight perf cost. The overhead is pretty small though, maxing at about 30-60µs, even in the worst cast 10000 candidates with smart case actually on.

Comment thread crates/file_finder/src/file_finder.rs
@yara-blue
yara-blue merged commit 722f308 into zed-industries:main Apr 17, 2026
31 checks passed
yara-blue added a commit that referenced this pull request Apr 20, 2026
…#54123)

Stacked on top of #54112
This is part 2 of 3 towards #51197
More details from the original PR #53551

This PR includes the changes from #54112 , im not sure how to avoid
that, my understanding is that after that one is merged, this PR can be
rebased onto main and everything will be correct. You can also view the
version of this that does reflect the changes more directly here:
feitreim#1

## Changes

In this PR I added a more general string matching functionality to
`fuzzy_nucleo`, in order to have proper testing for this, I also changed
the command palette, tab switching picker, branch picker, and recent
projects picker to use this new implementation. I think the command
palette change in particular is awesome, just super nice to vaguely
gesture at the command i want and have it pop right up.

The main change here and departure from
#37123 is realizing that the
primary reason for the regressions is actually how nucleo handles smart
case, the old `fuzzy` crate only uses the smart case argument to score
things differently, while nucleo actually filters on the case, eg. with
smart case query "Apple" wouldnt match "apple". To get around this we
always pass `CaseMatching::Ignore` to nucleo and implement the same
score modifications from fuzzy in our code.

There is a performance cost to that, of course, but from my testing it
is fairly static, not growing as the size increases, so maybe a query
takes 35 µs instead of 25 µs, but a query that takes 800 µs will only
take 820 µs.

Benchmark:
| kind | query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---|---:|---:|---:|---:|
  | string | 1-word | 100 | 9.15 µs | 24.6 µs | 0.37× |
  | string | 1-word | 1000 | 150.2 µs | 207.2 µs | 0.72× |
  | string | 1-word | 10000 | 1.34 ms | 2.07 ms | 0.65× |
  | string | 2-word | 100 | 5.16 µs | 2.94 µs | 1.75× |
  | string | 2-word | 1000 | 29.0 µs | 11.0 µs | 2.63× |
  | string | 2-word | 10000 | 210.6 µs | 55.5 µs | 3.79× |
  | string | 4-word | 100 | 2.57 µs | 2.33 µs | 1.10× |
  | string | 4-word | 1000 | 6.98 µs | 5.85 µs | 1.19× |
  | string | 4-word | 10000 | 20.0 µs | 12.0 µs | 1.66× |

When I added the 4-word queries to the benchmarks I was actually really
concerned that the performance would be awful, making it unsuitable for
the command palette especially. However, I think due to the CharBag
pre-filtering when the query is longer, the performance is actually way
better than the 2 word case.

Video:


https://github.com/user-attachments/assets/3cd7221b-424f-4fd3-8df1-5543dcc340a3

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Improved fuzzy matching in the command palette, branch picker, tab
switcher, and recent projects picker to support multi-word queries.

---------

Co-authored-by: Yara <git@yara.blue>
kathbigra pushed a commit to kathbigra/zed that referenced this pull request May 10, 2026
…enchmarks (zed-industries#54112)

This PR was originally a part of
zed-industries#53551 so theres more info
about its motivation there.

- Add a CharBag prefilter on path candidates to skip irrelevant entries
before invoking nucleo's matcher.
- Use binary_search on sorted matched char indices when reconstructing
byte positions (perf improvement).
- Add a criterion benchmark comparing `fuzzy_nucleo` path matching
against the existing fuzzy crate.

Performance Chart:

| Benchmark | Size | Nucleo (before) | Nucleo (after) | Fuzzy |
Before/Fuzzy | After/Fuzzy |

|-----------|-----:|----------------:|---------------:|------:|-------------:|------------:|
  | 1-word | 100 | 14.14 µs | 9.12 µs | 9.06 µs | 1.56x | 1.01x |
  | 1-word | 1,000 | 164.37 µs | 114.11 µs | 110.43 µs | 1.49x | 1.03x |
  | 1-word | 10,000 | 1.83 ms | 1.39 ms | 1.41 ms | 1.30x | 0.99x |
  | 2-word | 100 | 12.83 µs | 3.51 µs | 979 ns | 13.10x | 3.59x |
  | 2-word | 1,000 | 131.65 µs | 33.46 µs | 6.37 µs | 20.67x | 5.25x |
  | 2-word | 10,000 | 1.24 ms | 338.84 µs | 52.46 µs | 23.64x | 6.46x |o

Exact Current State:
| query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---:|---:|---:|---:|
  | 1-word | 100 | 8.62 µs | 9.22 µs | 0.93× |
  | 1-word | 1000 | 102 µs | 111 µs | 0.92× |
  | 1-word | 10000 | 1.13 ms | 1.28 ms | 0.88× |
  | 2-word | 100 | 3.48 µs | 0.98 µs | 3.55× |
  | 2-word | 1000 | 29.9 µs | 6.39 µs | 4.68× |
  | 2-word | 10000 | 271 µs | 53.4 µs | 5.08× |
  | 4-word | 100 | 0.85 µs | 0.53 µs | 1.60× |
  | 4-word | 1000 | 2.99 µs | 1.66 µs | 1.80× |
  | 4-word | 10000 | 20.1 µs | 9.14 µs | 2.20× |

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- fuzzy_nucleo: improved the performance of path matching
kathbigra pushed a commit to kathbigra/zed that referenced this pull request May 10, 2026
…zed-industries#54123)

Stacked on top of zed-industries#54112
This is part 2 of 3 towards zed-industries#51197
More details from the original PR zed-industries#53551

This PR includes the changes from zed-industries#54112 , im not sure how to avoid
that, my understanding is that after that one is merged, this PR can be
rebased onto main and everything will be correct. You can also view the
version of this that does reflect the changes more directly here:
feitreim#1

## Changes

In this PR I added a more general string matching functionality to
`fuzzy_nucleo`, in order to have proper testing for this, I also changed
the command palette, tab switching picker, branch picker, and recent
projects picker to use this new implementation. I think the command
palette change in particular is awesome, just super nice to vaguely
gesture at the command i want and have it pop right up.

The main change here and departure from
zed-industries#37123 is realizing that the
primary reason for the regressions is actually how nucleo handles smart
case, the old `fuzzy` crate only uses the smart case argument to score
things differently, while nucleo actually filters on the case, eg. with
smart case query "Apple" wouldnt match "apple". To get around this we
always pass `CaseMatching::Ignore` to nucleo and implement the same
score modifications from fuzzy in our code.

There is a performance cost to that, of course, but from my testing it
is fairly static, not growing as the size increases, so maybe a query
takes 35 µs instead of 25 µs, but a query that takes 800 µs will only
take 820 µs.

Benchmark:
| kind | query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---|---:|---:|---:|---:|
  | string | 1-word | 100 | 9.15 µs | 24.6 µs | 0.37× |
  | string | 1-word | 1000 | 150.2 µs | 207.2 µs | 0.72× |
  | string | 1-word | 10000 | 1.34 ms | 2.07 ms | 0.65× |
  | string | 2-word | 100 | 5.16 µs | 2.94 µs | 1.75× |
  | string | 2-word | 1000 | 29.0 µs | 11.0 µs | 2.63× |
  | string | 2-word | 10000 | 210.6 µs | 55.5 µs | 3.79× |
  | string | 4-word | 100 | 2.57 µs | 2.33 µs | 1.10× |
  | string | 4-word | 1000 | 6.98 µs | 5.85 µs | 1.19× |
  | string | 4-word | 10000 | 20.0 µs | 12.0 µs | 1.66× |

When I added the 4-word queries to the benchmarks I was actually really
concerned that the performance would be awful, making it unsuitable for
the command palette especially. However, I think due to the CharBag
pre-filtering when the query is longer, the performance is actually way
better than the 2 word case.

Video:


https://github.com/user-attachments/assets/3cd7221b-424f-4fd3-8df1-5543dcc340a3

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Improved fuzzy matching in the command palette, branch picker, tab
switcher, and recent projects picker to support multi-word queries.

---------

Co-authored-by: Yara <git@yara.blue>
Zenor27 pushed a commit to Zenor27/zed that referenced this pull request Jul 4, 2026
…enchmarks (zed-industries#54112)

This PR was originally a part of
zed-industries#53551 so theres more info
about its motivation there.

- Add a CharBag prefilter on path candidates to skip irrelevant entries
before invoking nucleo's matcher.
- Use binary_search on sorted matched char indices when reconstructing
byte positions (perf improvement).
- Add a criterion benchmark comparing `fuzzy_nucleo` path matching
against the existing fuzzy crate.

Performance Chart:

| Benchmark | Size | Nucleo (before) | Nucleo (after) | Fuzzy |
Before/Fuzzy | After/Fuzzy |

|-----------|-----:|----------------:|---------------:|------:|-------------:|------------:|
  | 1-word | 100 | 14.14 µs | 9.12 µs | 9.06 µs | 1.56x | 1.01x |
  | 1-word | 1,000 | 164.37 µs | 114.11 µs | 110.43 µs | 1.49x | 1.03x |
  | 1-word | 10,000 | 1.83 ms | 1.39 ms | 1.41 ms | 1.30x | 0.99x |
  | 2-word | 100 | 12.83 µs | 3.51 µs | 979 ns | 13.10x | 3.59x |
  | 2-word | 1,000 | 131.65 µs | 33.46 µs | 6.37 µs | 20.67x | 5.25x |
  | 2-word | 10,000 | 1.24 ms | 338.84 µs | 52.46 µs | 23.64x | 6.46x |o

Exact Current State:
| query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---:|---:|---:|---:|
  | 1-word | 100 | 8.62 µs | 9.22 µs | 0.93× |
  | 1-word | 1000 | 102 µs | 111 µs | 0.92× |
  | 1-word | 10000 | 1.13 ms | 1.28 ms | 0.88× |
  | 2-word | 100 | 3.48 µs | 0.98 µs | 3.55× |
  | 2-word | 1000 | 29.9 µs | 6.39 µs | 4.68× |
  | 2-word | 10000 | 271 µs | 53.4 µs | 5.08× |
  | 4-word | 100 | 0.85 µs | 0.53 µs | 1.60× |
  | 4-word | 1000 | 2.99 µs | 1.66 µs | 1.80× |
  | 4-word | 10000 | 20.1 µs | 9.14 µs | 2.20× |

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- fuzzy_nucleo: improved the performance of path matching
Zenor27 pushed a commit to Zenor27/zed that referenced this pull request Jul 4, 2026
…zed-industries#54123)

Stacked on top of zed-industries#54112
This is part 2 of 3 towards zed-industries#51197
More details from the original PR zed-industries#53551

This PR includes the changes from zed-industries#54112 , im not sure how to avoid
that, my understanding is that after that one is merged, this PR can be
rebased onto main and everything will be correct. You can also view the
version of this that does reflect the changes more directly here:
feitreim#1

## Changes

In this PR I added a more general string matching functionality to
`fuzzy_nucleo`, in order to have proper testing for this, I also changed
the command palette, tab switching picker, branch picker, and recent
projects picker to use this new implementation. I think the command
palette change in particular is awesome, just super nice to vaguely
gesture at the command i want and have it pop right up.

The main change here and departure from
zed-industries#37123 is realizing that the
primary reason for the regressions is actually how nucleo handles smart
case, the old `fuzzy` crate only uses the smart case argument to score
things differently, while nucleo actually filters on the case, eg. with
smart case query "Apple" wouldnt match "apple". To get around this we
always pass `CaseMatching::Ignore` to nucleo and implement the same
score modifications from fuzzy in our code.

There is a performance cost to that, of course, but from my testing it
is fairly static, not growing as the size increases, so maybe a query
takes 35 µs instead of 25 µs, but a query that takes 800 µs will only
take 820 µs.

Benchmark:
| kind | query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---|---:|---:|---:|---:|
  | string | 1-word | 100 | 9.15 µs | 24.6 µs | 0.37× |
  | string | 1-word | 1000 | 150.2 µs | 207.2 µs | 0.72× |
  | string | 1-word | 10000 | 1.34 ms | 2.07 ms | 0.65× |
  | string | 2-word | 100 | 5.16 µs | 2.94 µs | 1.75× |
  | string | 2-word | 1000 | 29.0 µs | 11.0 µs | 2.63× |
  | string | 2-word | 10000 | 210.6 µs | 55.5 µs | 3.79× |
  | string | 4-word | 100 | 2.57 µs | 2.33 µs | 1.10× |
  | string | 4-word | 1000 | 6.98 µs | 5.85 µs | 1.19× |
  | string | 4-word | 10000 | 20.0 µs | 12.0 µs | 1.66× |

When I added the 4-word queries to the benchmarks I was actually really
concerned that the performance would be awful, making it unsuitable for
the command palette especially. However, I think due to the CharBag
pre-filtering when the query is longer, the performance is actually way
better than the 2 word case.

Video:


https://github.com/user-attachments/assets/3cd7221b-424f-4fd3-8df1-5543dcc340a3

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Improved fuzzy matching in the command palette, branch picker, tab
switcher, and recent projects picker to support multi-word queries.

---------

Co-authored-by: Yara <git@yara.blue>
jonx pushed a commit to jonx/zed-aros that referenced this pull request Jul 17, 2026
…enchmarks (zed-industries#54112)

This PR was originally a part of
zed-industries#53551 so theres more info
about its motivation there.

- Add a CharBag prefilter on path candidates to skip irrelevant entries
before invoking nucleo's matcher.
- Use binary_search on sorted matched char indices when reconstructing
byte positions (perf improvement).
- Add a criterion benchmark comparing `fuzzy_nucleo` path matching
against the existing fuzzy crate.

Performance Chart:

| Benchmark | Size | Nucleo (before) | Nucleo (after) | Fuzzy |
Before/Fuzzy | After/Fuzzy |

|-----------|-----:|----------------:|---------------:|------:|-------------:|------------:|
  | 1-word | 100 | 14.14 µs | 9.12 µs | 9.06 µs | 1.56x | 1.01x |
  | 1-word | 1,000 | 164.37 µs | 114.11 µs | 110.43 µs | 1.49x | 1.03x |
  | 1-word | 10,000 | 1.83 ms | 1.39 ms | 1.41 ms | 1.30x | 0.99x |
  | 2-word | 100 | 12.83 µs | 3.51 µs | 979 ns | 13.10x | 3.59x |
  | 2-word | 1,000 | 131.65 µs | 33.46 µs | 6.37 µs | 20.67x | 5.25x |
  | 2-word | 10,000 | 1.24 ms | 338.84 µs | 52.46 µs | 23.64x | 6.46x |o

Exact Current State:
| query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---:|---:|---:|---:|
  | 1-word | 100 | 8.62 µs | 9.22 µs | 0.93× |
  | 1-word | 1000 | 102 µs | 111 µs | 0.92× |
  | 1-word | 10000 | 1.13 ms | 1.28 ms | 0.88× |
  | 2-word | 100 | 3.48 µs | 0.98 µs | 3.55× |
  | 2-word | 1000 | 29.9 µs | 6.39 µs | 4.68× |
  | 2-word | 10000 | 271 µs | 53.4 µs | 5.08× |
  | 4-word | 100 | 0.85 µs | 0.53 µs | 1.60× |
  | 4-word | 1000 | 2.99 µs | 1.66 µs | 1.80× |
  | 4-word | 10000 | 20.1 µs | 9.14 µs | 2.20× |

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- fuzzy_nucleo: improved the performance of path matching
jonx pushed a commit to jonx/zed-aros that referenced this pull request Jul 17, 2026
…zed-industries#54123)

Stacked on top of zed-industries#54112
This is part 2 of 3 towards zed-industries#51197
More details from the original PR zed-industries#53551

This PR includes the changes from zed-industries#54112 , im not sure how to avoid
that, my understanding is that after that one is merged, this PR can be
rebased onto main and everything will be correct. You can also view the
version of this that does reflect the changes more directly here:
feitreim#1

## Changes

In this PR I added a more general string matching functionality to
`fuzzy_nucleo`, in order to have proper testing for this, I also changed
the command palette, tab switching picker, branch picker, and recent
projects picker to use this new implementation. I think the command
palette change in particular is awesome, just super nice to vaguely
gesture at the command i want and have it pop right up.

The main change here and departure from
zed-industries#37123 is realizing that the
primary reason for the regressions is actually how nucleo handles smart
case, the old `fuzzy` crate only uses the smart case argument to score
things differently, while nucleo actually filters on the case, eg. with
smart case query "Apple" wouldnt match "apple". To get around this we
always pass `CaseMatching::Ignore` to nucleo and implement the same
score modifications from fuzzy in our code.

There is a performance cost to that, of course, but from my testing it
is fairly static, not growing as the size increases, so maybe a query
takes 35 µs instead of 25 µs, but a query that takes 800 µs will only
take 820 µs.

Benchmark:
| kind | query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---|---:|---:|---:|---:|
  | string | 1-word | 100 | 9.15 µs | 24.6 µs | 0.37× |
  | string | 1-word | 1000 | 150.2 µs | 207.2 µs | 0.72× |
  | string | 1-word | 10000 | 1.34 ms | 2.07 ms | 0.65× |
  | string | 2-word | 100 | 5.16 µs | 2.94 µs | 1.75× |
  | string | 2-word | 1000 | 29.0 µs | 11.0 µs | 2.63× |
  | string | 2-word | 10000 | 210.6 µs | 55.5 µs | 3.79× |
  | string | 4-word | 100 | 2.57 µs | 2.33 µs | 1.10× |
  | string | 4-word | 1000 | 6.98 µs | 5.85 µs | 1.19× |
  | string | 4-word | 10000 | 20.0 µs | 12.0 µs | 1.66× |

When I added the 4-word queries to the benchmarks I was actually really
concerned that the performance would be awful, making it unsuitable for
the command palette especially. However, I think due to the CharBag
pre-filtering when the query is longer, the performance is actually way
better than the 2 word case.

Video:


https://github.com/user-attachments/assets/3cd7221b-424f-4fd3-8df1-5543dcc340a3

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Improved fuzzy matching in the command palette, branch picker, tab
switcher, and recent projects picker to support multi-word queries.

---------

Co-authored-by: Yara <git@yara.blue>
@yara-blue yara-blue removed their assignment Aug 3, 2026
jolutz pushed a commit to jolutz/zed that referenced this pull request Aug 8, 2026
…enchmarks (zed-industries#54112)

This PR was originally a part of
zed-industries#53551 so theres more info
about its motivation there.

- Add a CharBag prefilter on path candidates to skip irrelevant entries
before invoking nucleo's matcher.
- Use binary_search on sorted matched char indices when reconstructing
byte positions (perf improvement).
- Add a criterion benchmark comparing `fuzzy_nucleo` path matching
against the existing fuzzy crate.

Performance Chart:

| Benchmark | Size | Nucleo (before) | Nucleo (after) | Fuzzy |
Before/Fuzzy | After/Fuzzy |

|-----------|-----:|----------------:|---------------:|------:|-------------:|------------:|
  | 1-word | 100 | 14.14 µs | 9.12 µs | 9.06 µs | 1.56x | 1.01x |
  | 1-word | 1,000 | 164.37 µs | 114.11 µs | 110.43 µs | 1.49x | 1.03x |
  | 1-word | 10,000 | 1.83 ms | 1.39 ms | 1.41 ms | 1.30x | 0.99x |
  | 2-word | 100 | 12.83 µs | 3.51 µs | 979 ns | 13.10x | 3.59x |
  | 2-word | 1,000 | 131.65 µs | 33.46 µs | 6.37 µs | 20.67x | 5.25x |
  | 2-word | 10,000 | 1.24 ms | 338.84 µs | 52.46 µs | 23.64x | 6.46x |o

Exact Current State:
| query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---:|---:|---:|---:|
  | 1-word | 100 | 8.62 µs | 9.22 µs | 0.93× |
  | 1-word | 1000 | 102 µs | 111 µs | 0.92× |
  | 1-word | 10000 | 1.13 ms | 1.28 ms | 0.88× |
  | 2-word | 100 | 3.48 µs | 0.98 µs | 3.55× |
  | 2-word | 1000 | 29.9 µs | 6.39 µs | 4.68× |
  | 2-word | 10000 | 271 µs | 53.4 µs | 5.08× |
  | 4-word | 100 | 0.85 µs | 0.53 µs | 1.60× |
  | 4-word | 1000 | 2.99 µs | 1.66 µs | 1.80× |
  | 4-word | 10000 | 20.1 µs | 9.14 µs | 2.20× |

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- fuzzy_nucleo: improved the performance of path matching
jolutz pushed a commit to jolutz/zed that referenced this pull request Aug 8, 2026
…zed-industries#54123)

Stacked on top of zed-industries#54112
This is part 2 of 3 towards zed-industries#51197
More details from the original PR zed-industries#53551

This PR includes the changes from zed-industries#54112 , im not sure how to avoid
that, my understanding is that after that one is merged, this PR can be
rebased onto main and everything will be correct. You can also view the
version of this that does reflect the changes more directly here:
feitreim#1

## Changes

In this PR I added a more general string matching functionality to
`fuzzy_nucleo`, in order to have proper testing for this, I also changed
the command palette, tab switching picker, branch picker, and recent
projects picker to use this new implementation. I think the command
palette change in particular is awesome, just super nice to vaguely
gesture at the command i want and have it pop right up.

The main change here and departure from
zed-industries#37123 is realizing that the
primary reason for the regressions is actually how nucleo handles smart
case, the old `fuzzy` crate only uses the smart case argument to score
things differently, while nucleo actually filters on the case, eg. with
smart case query "Apple" wouldnt match "apple". To get around this we
always pass `CaseMatching::Ignore` to nucleo and implement the same
score modifications from fuzzy in our code.

There is a performance cost to that, of course, but from my testing it
is fairly static, not growing as the size increases, so maybe a query
takes 35 µs instead of 25 µs, but a query that takes 800 µs will only
take 820 µs.

Benchmark:
| kind | query | size | nucleo | fuzzy | nucleo/fuzzy |
  |---|---|---:|---:|---:|---:|
  | string | 1-word | 100 | 9.15 µs | 24.6 µs | 0.37× |
  | string | 1-word | 1000 | 150.2 µs | 207.2 µs | 0.72× |
  | string | 1-word | 10000 | 1.34 ms | 2.07 ms | 0.65× |
  | string | 2-word | 100 | 5.16 µs | 2.94 µs | 1.75× |
  | string | 2-word | 1000 | 29.0 µs | 11.0 µs | 2.63× |
  | string | 2-word | 10000 | 210.6 µs | 55.5 µs | 3.79× |
  | string | 4-word | 100 | 2.57 µs | 2.33 µs | 1.10× |
  | string | 4-word | 1000 | 6.98 µs | 5.85 µs | 1.19× |
  | string | 4-word | 10000 | 20.0 µs | 12.0 µs | 1.66× |

When I added the 4-word queries to the benchmarks I was actually really
concerned that the performance would be awful, making it unsuitable for
the command palette especially. However, I think due to the CharBag
pre-filtering when the query is longer, the performance is actually way
better than the 2 word case.

Video:


https://github.com/user-attachments/assets/3cd7221b-424f-4fd3-8df1-5543dcc340a3

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Improved fuzzy matching in the command palette, branch picker, tab
switcher, and recent projects picker to support multi-word queries.

---------

Co-authored-by: Yara <git@yara.blue>
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 guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants