Skip to content

rustdoc search: prefer stable items in search results #141658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

lolbinarycat
Copy link
Contributor

fixes #138067

this does add a new field to the search index, but since we're only listing unstable items instead of adding a boolean flag to every item, it should only increase the search index size of sysroot crates, since those are the only ones using the staged_api feature, at least as far as the rust project is concerned.

@rustbot
Copy link
Collaborator

rustbot commented May 27, 2025

r? @GuillaumeGomez

rustbot has assigned @GuillaumeGomez.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-rustdoc-search Area: Rustdoc's search feature S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. labels May 27, 2025
@rustbot
Copy link
Collaborator

rustbot commented May 27, 2025

Some changes occurred in HTML/CSS/JS.

cc @GuillaumeGomez, @jsha, @lolbinarycat

@rust-log-analyzer

This comment has been minimized.

@lolbinarycat lolbinarycat force-pushed the rustdoc-search-stability-rank-138067 branch from e40eefb to d35829b Compare May 27, 2025 19:28
@GuillaumeGomez
Copy link
Member

GuillaumeGomez commented Jun 10, 2025

I'm concerned here about the search index size increase. In particular for very big crates like windows. Do you have some before/after numbers?

Nevermind, the only difference is a new array in the search index, so should be very limited, and only an impact on std/core/alloc crates.

// sort unstable items later
a = Number(
// @ts-expect-error
this.searchIndexUnstable.get(aaa.item.crate).contains(aaa.item.bitIndex),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of retrieving this information every time, why not storing this information in the related item when we build the search index?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, you're right, we should keep linear scans out of inner loops if possible.

if we have a binary search impl, or if we ever pull one in in the future, we could actually use that to speed up the unpacking time for the search index (since we can pre-sort them when generating the index), though it's probably not worth embedding a binary search impl for something that only speeds up the standard libraries.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, I copied this from the implementation of deprecated items sorting, so if we want to change one, we should probably change all of them (especially because those ones will be applicable to all crates).

Also, we have RoaringBitmap, so it's not a linear scan, actually.

I'm not sure what the memory impact of having 3 more boolean fields on each row is (especially when one of those is going to be unused in every non-sysroot crate), so I would kinda wanna do some degree of perf testing to see if this is worth it.

One thing we could do actually would be to add this data during transformResults, that way we're not adding a field to everything in the search index, only to the results that are currently being show, so we would only ever do 1 bitmap lookup per item per search, which may be better than doing it per comparsion, except for the fact that we're not doing it once per comparison, we're only doing it to compare when the items have the same edit distance, which is actually fairly likely to be less than the number of results.

TL;DR it's actually not obvious what the most performant solution would be, so I'd rather stick with what the code is already doing and then maybe make a followup PR focused on performance.

Copy link
Member

@GuillaumeGomez GuillaumeGomez Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this data when we build the search index seems like the way to go.

As for performance checking, @notriddle wrote a tool for it (which you can find here if I'm not wrong).

There are multiple ways of limiting the impact on performance. Another idea I had would be to put the new comparison in a function which would be created during search index creation: if we don't have any stability information, then the function is empty.

Anyway, it'll likely impact performance of all crates using rustdoc (both std/core/alloc and the others without stability info) so I'd really prefer to ensure what the impact is before approving it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another idea I had would be to put the new comparison in a function which would be created during search index creation: if we don't have any stability information, then the function is empty.

Is a dynamic function call faster than a searching an empty bitmap?
I suppose JITs have the advantage that they can inline closures dynamically, but I'm not sure how significant it would be.

I'm trying to run the perf tool, but i'm having a bit of trouble making a full custom toolchain, probably due to bootstrap settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it "working" and I think it's a bit out of date:

Testing T ... TypeError: searchModule.execQuery is not a function
    at Object.doSearch (/home/binarycat/src/rs/rustdoc-js-profile/src/tester.js:229:37)
    at main (/home/binarycat/src/rs/rustdoc-js-profile/src/tester.js:316:49)
    at Object.<anonymous> (/home/binarycat/src/rs/rustdoc-js-profile/src/tester.js:327:1)
    at Module._compile (node:internal/modules/cjs/loader:1734:14)
    at Object..js (node:internal/modules/cjs/loader:1899:10)
    at Module.load (node:internal/modules/cjs/loader:1469:32)
    at Module._load (node:internal/modules/cjs/loader:1286:12)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)
    at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:152:5)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue is still not fixed, right? (not the bench tool, the "unstable" value not being stored in the item)

@lolbinarycat
Copy link
Contributor Author

@GuillaumeGomez the windows crate will not see any size increase (beyond the 6 bytes of the empty u field), as it does not use thr staged_api feature, and we only store a list of unstable items.

i should look at how much bigger the standard library index is, thought.

@@ -736,6 +741,7 @@ pub(crate) fn build_index(
crate_data.serialize_field("r", &re_exports)?;
crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
crate_data.serialize_field("u", &bitmap_to_string(&unstable))?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add this field only if unstable is not empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could, although i'm pretty sure it would make most doc bundles bigger, as the logic for handling the undef field would be more than 6 bytes, even when minified (i believe this is why we don't omit other fields either, even tho many crates have no reexports or aliases)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll keep that in a corner of my memory until I have some idea on how to make it better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can get part of the way there by noticing js truthiness rules are just sane enough for this case, the only falsy string value is the empty string, so we can do | "" to cleanse the undef. we're still using a few chars for the field access though, so i think in order to make this profitable we have to put this in a loop and apply it to multiple fields. unfortunately our fields are not homogeneous, so we'll need a different default value for different fields, meaning we'd be barely breaking even in the average case.

honestly if we want to make the search index smaller, we're much better off chasing an improvement that scales, like delta compressing the row ids, instead of trying to save a single digit number of bytes.

@GuillaumeGomez
Copy link
Member

@GuillaumeGomez the windows crate will not see any size increase (beyond the 6 bytes of the empty u field), as it does not use thr staged_api feature, and we only store a list of unstable items.

i should look at how much bigger the standard library index is, thought.

Yeah, after re-reading, I realized I was wrong. So yeah, only impact should be in std/core/alloc crates. Would be nice to have some numbers for them. Although I don't expect the impact to be that big.

So seems like a very good start, just a few nits and it should be ready for merge.

@lolbinarycat
Copy link
Contributor Author

Based on my limited testing, this adds 8KiB to the std search index, which is currently at 1.3MiB, meaning this is an increase in size of 0.6%.

It's a bit worse in terms of compressed size, 0.8%, but it's still not super significant, but it's also only 1.8KB compressed.

@GuillaumeGomez
Copy link
Member

It's acceptable, but now we have numbers.

@lolbinarycat lolbinarycat force-pushed the rustdoc-search-stability-rank-138067 branch from d35829b to 341866a Compare June 10, 2025 18:36
@lolbinarycat
Copy link
Contributor Author

@GuillaumeGomez would it be acceptable to merge this as-is, then do followup perf testing about the best way to handle unstable, deprecated, and empty description items is? I have a hunch that the short circuiting nature of search result sorting might mean this is a situation where worst case algorithmic complexity is not the factor that matters in practice.

@@ -642,6 +643,7 @@ pub(crate) fn build_index(
let mut parents_backref_queue = VecDeque::new();
let mut functions = String::with_capacity(self.items.len());
let mut deprecated = Vec::with_capacity(self.items.len());
let mut unstable = Vec::with_capacity(self.items.len());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that the way we pre-allocate everything is pretty bad since we have x times the necessary memory used. Well, this one can be tweaked in a follow-up.

@rustbot
Copy link
Collaborator

rustbot commented Aug 8, 2025

⚠️ Warning ⚠️

@lolbinarycat lolbinarycat force-pushed the rustdoc-search-stability-rank-138067 branch from e15e1b2 to fdbc8d0 Compare August 8, 2025 16:55
@GuillaumeGomez
Copy link
Member

Thanks!

@bors r+ rollup

@bors
Copy link
Collaborator

bors commented Aug 8, 2025

📌 Commit fdbc8d0 has been approved by GuillaumeGomez

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 8, 2025
@lolbinarycat lolbinarycat added the relnotes Marks issues that should be documented in the release notes of the next release. label Aug 8, 2025
bors added a commit that referenced this pull request Aug 9, 2025
Rollup of 23 pull requests

Successful merges:

 - #141658 (rustdoc search: prefer stable items in search results)
 - #141828 (Add diagnostic explaining STATUS_STACK_BUFFER_OVERRUN not only being used for stack buffer overruns if link.exe exits with that exit code)
 - #144823 (coverage: Extract HIR-related helper code out of the main module)
 - #144883 (Remove unneeded `drop_in_place` calls)
 - #144923 (Move several more float tests to floats/mod.rs)
 - #144988 (Add annotations to the graphviz region graph on region origins)
 - #145010 (Couple of minor abi handling cleanups)
 - #145017 (Explicitly disable vector feature on s390x baseline of bad-reg test)
 - #145027 (Optimize `char::is_alphanumeric`)
 - #145050 (add member constraints tests)
 - #145073 (update enzyme submodule to handle llvm 21)
 - #145080 (Escape diff strings in MIR dataflow graphviz)
 - #145082 (Fix some bad formatting in `-Zmacro-stats` output.)
 - #145083 (Fix cross-compilation of Cargo)
 - #145096 (Fix wasm target build with atomics feature)
 - #145097 (remove unnecessary `TypeFoldable` impls)
 - #145100 (Rank doc aliases lower than equivalently matched items)
 - #145103 (rustc_metadata: remove unused private trait impls)
 - #145115 (defer opaque type errors, generally greatly reduce tainting)
 - #145119 (rustc_public: fix missing parenthesis in pretty discriminant)
 - #145124 (Recover `for PAT = EXPR {}`)
 - #145132 (Refactor map_unit_fn lint)
 - #145134 (Reduce indirect assoc parent queries)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit that referenced this pull request Aug 9, 2025
Rollup of 23 pull requests

Successful merges:

 - #141658 (rustdoc search: prefer stable items in search results)
 - #141828 (Add diagnostic explaining STATUS_STACK_BUFFER_OVERRUN not only being used for stack buffer overruns if link.exe exits with that exit code)
 - #144823 (coverage: Extract HIR-related helper code out of the main module)
 - #144883 (Remove unneeded `drop_in_place` calls)
 - #144923 (Move several more float tests to floats/mod.rs)
 - #144988 (Add annotations to the graphviz region graph on region origins)
 - #145010 (Couple of minor abi handling cleanups)
 - #145017 (Explicitly disable vector feature on s390x baseline of bad-reg test)
 - #145027 (Optimize `char::is_alphanumeric`)
 - #145050 (add member constraints tests)
 - #145073 (update enzyme submodule to handle llvm 21)
 - #145080 (Escape diff strings in MIR dataflow graphviz)
 - #145082 (Fix some bad formatting in `-Zmacro-stats` output.)
 - #145083 (Fix cross-compilation of Cargo)
 - #145096 (Fix wasm target build with atomics feature)
 - #145097 (remove unnecessary `TypeFoldable` impls)
 - #145100 (Rank doc aliases lower than equivalently matched items)
 - #145103 (rustc_metadata: remove unused private trait impls)
 - #145115 (defer opaque type errors, generally greatly reduce tainting)
 - #145119 (rustc_public: fix missing parenthesis in pretty discriminant)
 - #145124 (Recover `for PAT = EXPR {}`)
 - #145132 (Refactor map_unit_fn lint)
 - #145134 (Reduce indirect assoc parent queries)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 48f5929 into rust-lang:master Aug 9, 2025
10 checks passed
@rustbot rustbot added this to the 1.91.0 milestone Aug 9, 2025
rust-timer added a commit that referenced this pull request Aug 9, 2025
Rollup merge of #141658 - lolbinarycat:rustdoc-search-stability-rank-138067, r=GuillaumeGomez

rustdoc search: prefer stable items in search results

fixes #138067

this does add a new field to the search index, but since we're only listing unstable items instead of adding a boolean flag to every item, it should only increase the search index size of sysroot crates, since those are the only ones using the `staged_api` feature, at least as far as the rust project is concerned.
@jieyouxu
Copy link
Member

jieyouxu commented Aug 9, 2025

Hi bors this already merged
@bors r-

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Aug 9, 2025
@jieyouxu
Copy link
Member

jieyouxu commented Aug 9, 2025

I am bors
@rustbot label: -S-waiting-on-author +S-waiting-on-bors +merged-by-bors

@rustbot rustbot added merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-rustdoc-search Area: Rustdoc's search feature merged-by-bors This PR was explicitly merged by bors. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[rustdoc search] sort stable items above unstable ones
6 participants