-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Do more lazy-formatting in librustdoc
🦥
#136828
Conversation
rustbot has assigned @GuillaumeGomez. Use |
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…r=<try> Do more lazy-formatting in `librustdoc` 🦥 Modify some formatting to be lazy, i.e. to not allocate interim strings that are later formatted into different strings. Commits are small and stand on their own, and should mostly compile separately. (The first one doesn't compile due to `dead_code` because all it does is introduce a helper used in later commits) Really excited about this one, local perf results are really good. I'd love a perf run to see how this looks on CI. This is the comparison of `instructions:u` count between master and this PR, on my computer: # Summary | | Range | Mean | Count | |:---:|:---:|:---:|:---:| | Regressions | - | 0.00% | 0 | | Improvements | -8.03%, -0.40% | -2.93% | 5 | | All | -8.03%, -0.40% | -2.93% | 5 | # Primary benchmarks | Benchmark | Profile | Scenario | % Change | Significance Factor | |:---:|:---:|:---:|:---:|:---:| | typenum-1.17.0 | doc | full | -8.03% | 40.16x | | nalgebra-0.33.0 | doc | full | -4.19% | 20.97x | | stm32f4-0.14.0 | doc | full | -1.35% | 6.73x | | libc-0.2.124 | doc | full | -0.67% | 3.33x | | cranelift-codegen-0.82.1 | doc | full | -0.40% | 1.99x |
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Addressed comment in a new commit so that history is kept intact, I'll rebase if this gets approved :) |
Finished benchmarking commit (a804f1e): comparison URL. Overall result: ✅ improvements - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.
Max RSS (memory usage)Results (primary -3.5%, secondary -3.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary -6.8%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 779.051s -> 779.064s (0.00%) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this, the perf numbers look really good!
I think there's a couple of places where fmt::from_fn
could be replaced with the simpler format_args!
, but other than that, it all looks good.
if has_more_content { | ||
let link = format!(" <a{}>Read more</a>", assoc_href_attr(item, link, cx)); | ||
let link = if has_more_content { | ||
let link = fmt::from_fn(|f| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: This can be made more consise with format_args!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't work either...
// If there is no `href` for the reason explained above, simply do not render it which is valid: | ||
// https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements | ||
href.map(|href| format!(" href=\"{href}\"")).unwrap_or_default() | ||
Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\""))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto (I think)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't work either 😅
write!( | ||
f, | ||
" <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>", | ||
ty = Escape(&format!("{:#}", ty.print(cx))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not at all needed in this PR, but I wounder if this temporary allocation here can also go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was wondering that too. I think it's unavoidable, since Escape
needs to actually go over the formatted string and escape certain characters. The only way to create a "lazy" Escape
construct would be to modify std's Formatter
, which obviously is not an option...
But maybe I'm missing something.
}; | ||
let aliases = (!aliases.is_empty()) | ||
.then_some(fmt::from_fn(|f| { | ||
write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto wrt format_args!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, doesn't work...
error[E0716]: temporary value dropped while borrowed
--> src/librustdoc/html/render/mod.rs:2111:20
|
2111 | .then_some(format_args!(
| ____________________-
2112 | | " data-aliases=\"{}\"",
2113 | | fmt::from_fn(|f| aliases.iter().joined(",", f))
2114 | | ))
| | ^
| | |
| |_________creates a temporary value which is freed while still in use
| in this macro invocation
2115 | .maybe_display();
| - temporary value is freed at the end of this statement
2116 | write!(w, "<section id=\"{id}\" class=\"impl\"{aliases}>");
| --------- borrow later used here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, this is apparently a known limitation. Sorry 😔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, I can never remember exactly when it can be used and when not, so I was happy to double check 😊
src/librustdoc/display.rs
Outdated
@@ -1,3 +1,5 @@ | |||
//! Various utilities for working with [`fmt::Display`](std::fmt::Display) implementations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️ for writing a doc-comment on an internal module.
r=me with git history cleaned up |
8f77596
to
96eb329
Compare
@aDotInTheVoid rebased and CI is green 😁 |
Thanks! @bors=aDotInTheVoid,GuillaumeGomez rollup=iffy |
Sigh, way too early for me apparently... @bors r=aDotInTheVoid,GuillaumeGomez rollup=iffy |
☔ The latest upstream changes (presumably #136943) made this pull request unmergeable. Please resolve the merge conflicts. |
96eb329
to
ca1987a
Compare
@bors r=aDotInTheVoid,GuillaumeGomez rollup=iffy |
💡 This pull request was already approved, no need to approve it again.
|
…r=GuillaumeGomez,aDotInTheVoid Do more lazy-formatting in `librustdoc` 🦥 Modify some formatting to be lazy, i.e. to not allocate interim strings that are later formatted into different strings. Commits are small and stand on their own, and should mostly compile separately. (The first one doesn't compile due to `dead_code` because all it does is introduce a helper used in later commits) Really excited about this one, local perf results are really good. I'd love a perf run to see how this looks on CI. This is the comparison of `instructions:u` count between master and this PR, on my computer: # Summary | | Range | Mean | Count | |:---:|:---:|:---:|:---:| | Regressions | - | 0.00% | 0 | | Improvements | -8.03%, -0.40% | -2.93% | 5 | | All | -8.03%, -0.40% | -2.93% | 5 | # Primary benchmarks | Benchmark | Profile | Scenario | % Change | Significance Factor | |:---:|:---:|:---:|:---:|:---:| | typenum-1.17.0 | doc | full | -8.03% | 40.16x | | nalgebra-0.33.0 | doc | full | -4.19% | 20.97x | | stm32f4-0.14.0 | doc | full | -1.35% | 6.73x | | libc-0.2.124 | doc | full | -0.67% | 3.33x | | cranelift-codegen-0.82.1 | doc | full | -0.40% | 1.99x |
💔 Test failed - checks-actions |
This comment was marked as outdated.
This comment was marked as outdated.
A job failed! Check out the build log: (web) (plain) Click to see the possible cause of the failure (guessed by this bot)
|
☀️ Test successful - checks-actions |
Finished benchmarking commit (69fd5e4): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.
Max RSS (memory usage)Results (primary 5.5%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary -7.0%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 790.255s -> 789.995s (-0.03%) |
…<try> Allow lazy HTML escaping Inspired by [this comment](rust-lang#136828 (comment)) by `@aDotInTheVoid` . Makes `Escape` and `EscapeBodyText` accept any `impl fmt::Display`, instead of a `&str`, which allows us to avoid a few interim `String` allocations. This opens up room for more lazifying, but I'll keep those for a separate PR. Unfortunately, I think there might be a hit to performance because of the double vtable-indirection caused by wrapping a `fmt::Formatter` in another one, but I think that I should be able to gain that perf back by doing more lazy printing (either the small things improvements I made in this PR, or in later ones) Probably better to review each commit individually.
…<try> Allow lazy HTML escaping Inspired by [this comment](rust-lang#136828 (comment)) by `@aDotInTheVoid` . Makes `Escape` and `EscapeBodyText` accept any `impl fmt::Display`, instead of a `&str`, which allows us to avoid a few interim `String` allocations. This opens up room for more lazifying, but I'll keep those for a separate PR. Unfortunately, I think there might be a hit to performance because of the double vtable-indirection caused by wrapping a `fmt::Formatter` in another one, but I think that I should be able to gain that perf back by doing more lazy printing (either the small things improvements I made in this PR, or in later ones) Probably better to review each commit individually.
Modify some formatting to be lazy, i.e. to not allocate interim strings that are later formatted into different strings.
Commits are small and stand on their own, and should mostly compile separately. (The first one doesn't compile due to
dead_code
because all it does is introduce a helper used in later commits)Really excited about this one, local perf results are really good. I'd love a perf run to see how this looks on CI. This is the comparison of
instructions:u
count between master and this PR, on my computer:Summary
Primary benchmarks