-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Improve page load performance in rustdoc #82315
Conversation
Some changes occurred in HTML/CSS/JS. |
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @ollie27 (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
Wouldn't |
Overall, it's all very nice changes. My only concern here is about inling the font css: I don't see much point for it and it'll make all HTML pages heavier for a small gain, if any. |
I'm happy to switch to link preload. Here's the rationale I wrote in a commit (and then forgot to hoist to the PR desc, whoops):
But it is indeed a slight benefit. I suspect most people don't have those fonts installed. I'll push a preload variant. |
I've updated the demo pages at https://jacob.hoffman-andrews.com/rust/flow-improvements/std/string/struct.String.html. |
My concern remains. ;) |
As in, the Here's why I think it's useful: Fonts are two levels deep in the page load hierarchy. The browser has to load the CSS before it even knows what URLs to fetch for the fonts. This leads to inefficient utilization of the network - the browser could have sent out requests for CSS and fonts in parallel, but now it has to load them in serial (relative to the CSS). It's a gain for the first load, though I think what you're getting at is that it may not be much of a gain for loads when the fonts are already in a disk cache? A couple of resources. Unfortunately neither directly addresses the issue of how helpful preloading is when the resource is already on disk. https://web.dev/preload-critical-assets/ |
Also worth mentioning #82286: static resources on doc.rust-lang.org currently don't have cache headers and get a round-trip with a 304 on every page load. That's easily fixed for doc.rust-lang.org, but other sites hosting rustdoc might have the same problem (which is a web server config problem rather than a rustdoc problem). For sites that have this problem, preloading does improve performance even if the fonts are on disk already. |
IIRC |
It's maybe "only" 664 bytes, but it has to be multiplied by the number of pages, and there are a lot. To be noted for later: in case we decide to go on with this CSS font rule inlining, let's remove the CSS font file and simply create a function in the source code to get the CSS font rules directly. No need to keep a file around if it's only to be used at compile time. |
So, in other words, something like this? fn generates_fonts_css() -> String {
use std::fmt::Write;
let fonts = [
("Fira Sans", "normal", "400", "Fira Sans", "FiraSans-Regular.woff"),
("Fira Sans", "normal", "500", "Fira Sans Medium", "FiraSans-Medium.woff"),
("Source Serif Pro", "normal", "400", "Source Serif Pro", "SourceSerifPro-Regular.ttf.woff"),
("Source Serif Pro", "italic", "400", "Source Serif Pro Italic", "SourceSerifPro-It.ttf.woff"),
("Source Serif Pro", "normal", "700", "Source Serif Pro Bold", "SourceSerifPro-Bold.ttf.woff"),
("Source Code Pro", "normal", "400", "", "SourceCodePro-Regular.ttf.woff"),
("Source Code Pro", "normal", "600", "", "SourceCodePro-Semibold.ttf.woff"),
];
let mut ret_val = "/*(C) info FONT_LICENSES.txt*/".to_string();
for (family, style, weight, local, url) in &fonts[..] {
write!(&mut ret_val, "@font-face{{font-family:'{family}';font-style:{style};font-weight:{weight};src:{maybe_local}url({url})}}",
family = family,
style = style,
weight = weight,
maybe_local = (if *local != "" { format!("local('{}'),", local) } else { String::new() }),
url = url
).expect("writing to string is infallible");
}
assert!(ret_val.len() < 1000); // the exact value is 939, BTW
ret_val
}
lazy_static!{
static ref FONTS_CSS: String = generate_fonts_css();
} We could make it even smaller by renaming the fonts ( |
@notriddle That too (I didn't look at the new |
I pushed a new revision and demo. When I switched to
Thanks for the links to the history here, @Nemo157. I read through these. It looks like the first attempt was with It looks like #72557 was later updated to use To be clear about possible downsides: If font loading is slow, users may see a reflow when the fonts come in. However, the status quo is that users see a blank page until the fonts come in, and then get a reflow anyhow. And preloading and cache headers should greatly reduce the likelihood of seeing a reflow. |
Personally, I would rather just inline the CSS, not use preload links. It’s not that much bigger, and it’s less total code. |
@notriddle Don't have much of a preference and I'm clearly not a front-end expert. What do you think about it @Nemo157 ? |
I personally err on the side of inlining more stuff, most of my personal websites target 1 request to load; a few extra bytes in the request that is going to happen anyway seem less expensive than doing an extra request. |
☔ The latest upstream changes (presumably #82552) made this pull request unmergeable. Please resolve the merge conflicts. |
#82545 means we should really do the inlined CSS version, since For the question of whether the inlined CSS should be a file, or should be a string in .rs file, or should be an |
As effectively already enumerated by @jsha: While adding |
a77b143
to
c1f7ff2
Compare
Add font-display: swap. Per https://web.dev/font-display/, this prevents "flash of invisible text" during load by using a system font until the custom font is available. I've noticed this flash of invisible text occasionally when reading Rust docs. Add an explicit height to icons (which already had an explicit width) to allow browsers to lay out the page more accurately before the icons have been loaded. https://web.dev/optimize-cls/. Add min-width: 115px to the crate search dropdown. When the HTML first loads, this dropdown includes only the text "All crates." Later, JS loads the items underneath it, some of which are wider. That causes the dropdown to get wider, causing a distracting reflow. This sets a min-width based on the size that the dropdown eventually becomes based on the crates on doc.rust-lang.org, reducing page movement during load.
On most platforms and browsers, `sans-serif` is equivalent to Arial. However, on Firefox on Ubuntu (and possibly other Linuxes), `sans-serif` is DejaVu Sans, a much wider font. This creates a larger shift in text when the custom fonts finally load. Arial is a web-safe font, and specifying it explicitly gives us more cross-platform consistency, as well as reducing the layout shift that happens when fonts load.
I pushed a small update: f9cfe15 uses Arial in place of sans-serif, which is a no-op except on Firefox, where it makes things more consistent with other platforms and reduces the size of the reflow when fonts load. |
Thanks! @bors: r+ rollup |
📌 Commit f9cfe15 has been approved by |
…Gomez Improve page load performance in rustdoc Add an explicit height to icons (which already had an explicit width) to allow browsers to lay out the page more accurately before the icons have been loaded. https://web.dev/optimize-cls/. Add min-width: 115px to the crate search dropdown. When the HTML first loads, this dropdown includes only the text "All crates." Later, JS loads the items underneath it, some of which are wider. That causes the dropdown to get wider, causing a distracting reflow. This sets a min-width based on the size that the dropdown eventually becomes based on the crates on doc.rust-lang.org, reducing page movement during load. Add font-display: swap. Per https://web.dev/font-display/, this prevents "flash of invisible text" during load by using a system font until the custom font is available. I've noticed this flash of invisible text occasionally when reading Rust docs. Note that users without cached fonts will see text, and then see it reflow. For `docs.rust-lang.org`, [setting caching headers will help a lot](rust-lang/simpleinfra#62). Generated output at https://jacob.hoffman-andrews.com/rust/flow-improvements/std/string/struct.String.html.
…Gomez Improve page load performance in rustdoc Add an explicit height to icons (which already had an explicit width) to allow browsers to lay out the page more accurately before the icons have been loaded. https://web.dev/optimize-cls/. Add min-width: 115px to the crate search dropdown. When the HTML first loads, this dropdown includes only the text "All crates." Later, JS loads the items underneath it, some of which are wider. That causes the dropdown to get wider, causing a distracting reflow. This sets a min-width based on the size that the dropdown eventually becomes based on the crates on doc.rust-lang.org, reducing page movement during load. Add font-display: swap. Per https://web.dev/font-display/, this prevents "flash of invisible text" during load by using a system font until the custom font is available. I've noticed this flash of invisible text occasionally when reading Rust docs. Note that users without cached fonts will see text, and then see it reflow. For `docs.rust-lang.org`, [setting caching headers will help a lot](rust-lang/simpleinfra#62). Generated output at https://jacob.hoffman-andrews.com/rust/flow-improvements/std/string/struct.String.html.
Rollup of 8 pull requests Successful merges: - rust-lang#80527 (Make rustdoc lints a tool lint instead of built-in) - rust-lang#82310 (Load rustdoc's JS search index on-demand.) - rust-lang#82315 (Improve page load performance in rustdoc) - rust-lang#82564 (Revert `Vec::spare_capacity_mut` impl to prevent pointers invalidation) - rust-lang#82697 (Fix stabilization version of move_ref_pattern) - rust-lang#82717 (Account for macros when suggesting adding lifetime) - rust-lang#82740 (Fix commit detected when using `download-rustc`) - rust-lang#82744 (Pass `CrateNum` by value instead of by reference) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Note that removing the Web-safe fonts are not a thing, apparently (TIL). |
Thanks for the update, @nagisa! Today I also Learned the web-safe fonts are not a thing. Want to share a link or two? Also, what systems are you finding that don't have |
I've a NixOS (Linux) system, though I imagine people using any "configure yourself from scratch" kind of distribution like, say, Arch Linux would be affected too. As far as Firefox is concerned, there's a feature where users can disallow websites to select fonts other than |
@@ -475,7 +482,7 @@ h4 > code, h3 > code, .invisible > code { | |||
} | |||
#main > .since { | |||
top: inherit; | |||
font-family: "Fira Sans", sans-serif; | |||
font-family: "Fira Sans", Arial; |
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.
Shouldn't we keep sans-serif as Arial may not always be available?
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.
That sounds logical indeed. Want to send a PR for it?
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.
Oh, it's fixed. 7134b0e
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.
Awesome!
rustdoc: preload fonts Follow-up from rust-lang#82315. I noticed that font loading was so slow that even when loading from local disk, we get a flash of unstyled text (FOUT) followed by a reflow when the fonts load. With this change, we reliably get the appropriate fonts in the first render pass when loading locally, and we get it some of the time when loading from a website. This only preloads woff2 versions. According to https://caniuse.com/?search=preload and https://caniuse.com/?search=woff2, all browsers that support preload also support woff2, so this is fine; we will never load two copies of a font. Don't preload italic font faces because they aren't used on all pages. Demo: https://rustdoc.crud.net/jsha/preload-fonts/std/string/struct.String.html
rustdoc: preload fonts Follow-up from rust-lang#82315. I noticed that font loading was so slow that even when loading from local disk, we get a flash of unstyled text (FOUT) followed by a reflow when the fonts load. With this change, we reliably get the appropriate fonts in the first render pass when loading locally, and we get it some of the time when loading from a website. This only preloads woff2 versions. According to https://caniuse.com/?search=preload and https://caniuse.com/?search=woff2, all browsers that support preload also support woff2, so this is fine; we will never load two copies of a font. Don't preload italic font faces because they aren't used on all pages. Demo: https://rustdoc.crud.net/jsha/preload-fonts/std/string/struct.String.html
Add an explicit height to icons (which already had an explicit width) to allow browsers to lay out the page more accurately before the icons have been loaded. https://web.dev/optimize-cls/.
Add min-width: 115px to the crate search dropdown. When the HTML first loads, this dropdown includes only the text "All crates." Later, JS loads the items underneath it, some of which are wider. That causes the dropdown to get wider, causing a distracting reflow. This sets a min-width based on the size that the dropdown eventually becomes based on the crates on doc.rust-lang.org, reducing page movement during load.
Add font-display: swap. Per https://web.dev/font-display/, this prevents "flash of invisible text" during load by using a system font until the custom font is available. I've noticed this flash of invisible text occasionally when reading Rust docs. Note that users without cached fonts will see text, and then see it reflow. For
docs.rust-lang.org
, setting caching headers will help a lot.Generated output at https://jacob.hoffman-andrews.com/rust/flow-improvements/std/string/struct.String.html.