Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 62b72bd

Browse files
Rollup merge of #147189 - yotamofek:pr/rustdoc/highlight-optimizations-2, r=GuillaumeGomez
Replace `rustc_span::Span` with a stripped down version for librustdoc's highlighter While profiling rustdoc's syntax highlighter, I noticed a lot of time being spent in the `Span` interner, due to the highlighter creating a lot of (new) spans. Since the only data from the `Span` that we use is the `hi` and `lo` byte positions - I replaced the regular `Span` with a simple one with two fields, and in my benchmarks it seemed to make a big dent in the highlighter's perf, so thought I would see what the perf runner says.
2 parents ca8ed7e + 2d03ab1 commit 62b72bd

File tree

7 files changed

+80
-19
lines changed

7 files changed

+80
-19
lines changed

‎src/bootstrap/src/core/builder/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ impl<'a> Builder<'a> {
11451145
test::RunMakeCargo,
11461146
),
11471147
Kind::Miri => describe!(test::Crate),
1148-
Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
1148+
Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc),
11491149
Kind::Doc => describe!(
11501150
doc::UnstableBook,
11511151
doc::UnstableBookGen,

‎src/librustdoc/html/highlight.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@ use std::iter;
1212

1313
use rustc_data_structures::fx::FxIndexMap;
1414
use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
15+
use rustc_span::BytePos;
1516
use rustc_span::edition::Edition;
1617
use rustc_span::symbol::Symbol;
17-
use rustc_span::{BytePos, DUMMY_SP, Span};
1818

1919
use super::format;
2020
use crate::clean::PrimitiveType;
2121
use crate::display::Joined as _;
2222
use crate::html::escape::EscapeBodyText;
2323
use crate::html::macro_expansion::ExpandedCode;
24+
use crate::html::render::span_map::{DUMMY_SP, Span};
2425
use crate::html::render::{Context, LinkFromSrc};
2526

2627
/// This type is needed in case we want to render links on items to allow to go to their definition.

‎src/librustdoc/html/highlight/tests.rs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use expect_test::expect_file;
22
use rustc_data_structures::fx::FxIndexMap;
33
use rustc_span::create_default_session_globals_then;
4+
use test::Bencher;
45

56
use super::{DecorationInfo, write_code};
67

@@ -81,3 +82,16 @@ let a = 4;";
8182
expect_file!["fixtures/decorations.html"].assert_eq(&html);
8283
});
8384
}
85+
86+
#[bench]
87+
fn bench_html_highlighting(b: &mut Bencher) {
88+
let src = include_str!("../../../../compiler/rustc_ast/src/visit.rs");
89+
90+
create_default_session_globals_then(|| {
91+
b.iter(|| {
92+
let mut out = String::new();
93+
write_code(&mut out, src, None, None, None);
94+
out
95+
});
96+
});
97+
}

‎src/librustdoc/html/render/context.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::formats::item_type::ItemType;
3030
use crate::html::escape::Escape;
3131
use crate::html::macro_expansion::ExpandedCode;
3232
use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary};
33+
use crate::html::render::span_map::Span;
3334
use crate::html::render::write_shared::write_shared;
3435
use crate::html::url_parts_builder::UrlPartsBuilder;
3536
use crate::html::{layout, sources, static_files};
@@ -139,7 +140,7 @@ pub(crate) struct SharedContext<'tcx> {
139140

140141
/// Correspondence map used to link types used in the source code pages to allow to click on
141142
/// links to jump to the type's definition.
142-
pub(crate) span_correspondence_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
143+
pub(crate) span_correspondence_map: FxHashMap<Span, LinkFromSrc>,
143144
pub(crate) expanded_codes: FxHashMap<BytePos, Vec<ExpandedCode>>,
144145
/// The [`Cache`] used during rendering.
145146
pub(crate) cache: Cache,

‎src/librustdoc/html/render/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod ordered_json;
3636
mod print_item;
3737
pub(crate) mod sidebar;
3838
mod sorted_template;
39-
mod span_map;
39+
pub(crate)mod span_map;
4040
mod type_layout;
4141
mod write_shared;
4242

‎src/librustdoc/html/render/span_map.rs‎

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,48 @@ use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath};
88
use rustc_middle::hir::nested_filter;
99
use rustc_middle::ty::TyCtxt;
1010
use rustc_span::hygiene::MacroKind;
11-
use rustc_span::{BytePos, ExpnKind,Span};
11+
use rustc_span::{BytePos, ExpnKind};
1212

1313
use crate::clean::{self, PrimitiveType, rustc_span};
1414
use crate::html::sources;
1515

16+
/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end byte positions of the span.
17+
///
18+
/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting, and since we
19+
/// never actually use the context and parent that are stored in a normal `Span`, we can replace its usages with this
20+
/// one, which is much cheaper to construct.
21+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22+
pub(crate) struct Span {
23+
lo: BytePos,
24+
hi: BytePos,
25+
}
26+
27+
impl From<rustc_span::Span> for Span {
28+
fn from(value: rustc_span::Span) -> Self {
29+
Self { lo: value.lo(), hi: value.hi() }
30+
}
31+
}
32+
33+
impl Span {
34+
pub(crate) fn lo(self) -> BytePos {
35+
self.lo
36+
}
37+
38+
pub(crate) fn hi(self) -> BytePos {
39+
self.hi
40+
}
41+
42+
pub(crate) fn with_lo(self, lo: BytePos) -> Self {
43+
Self { lo, hi: self.hi() }
44+
}
45+
46+
pub(crate) fn with_hi(self, hi: BytePos) -> Self {
47+
Self { lo: self.lo(), hi }
48+
}
49+
}
50+
51+
pub(crate) const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0) };
52+
1653
/// This enum allows us to store two different kinds of information:
1754
///
1855
/// In case the `span` definition comes from the same crate, we can simply get the `span` and use
@@ -96,7 +133,7 @@ impl SpanMapVisitor<'_> {
96133
})
97134
.unwrap_or(path.span)
98135
};
99-
self.matches.insert(span, link);
136+
self.matches.insert(span.into(), link);
100137
}
101138
Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => {
102139
let path_span = if only_use_last_segment
@@ -106,11 +143,12 @@ impl SpanMapVisitor<'_> {
106143
} else {
107144
path.span
108145
};
109-
self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span)));
146+
self.matches.insert(path_span.into(), LinkFromSrc::Local(clean::Span::new(span)));
110147
}
111148
Res::PrimTy(p) => {
112149
// FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
113-
self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
150+
self.matches
151+
.insert(path.span.into(), LinkFromSrc::Primitive(PrimitiveType::from(p)));
114152
}
115153
Res::Err => {}
116154
_ => {}
@@ -127,7 +165,7 @@ impl SpanMapVisitor<'_> {
127165
if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE {
128166
return;
129167
}
130-
self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id()));
168+
self.matches.insert(span.into(), LinkFromSrc::Doc(item.owner_id.to_def_id()));
131169
}
132170
}
133171

@@ -138,7 +176,7 @@ impl SpanMapVisitor<'_> {
138176
/// so, we loop until we find the macro definition by using `outer_expn_data` in a loop.
139177
/// Finally, we get the information about the macro itself (`span` if "local", `DefId`
140178
/// otherwise) and store it inside the span map.
141-
fn handle_macro(&mut self, span: Span) -> bool {
179+
fn handle_macro(&mut self, span: rustc_span::Span) -> bool {
142180
if !span.from_expansion() {
143181
return false;
144182
}
@@ -176,7 +214,7 @@ impl SpanMapVisitor<'_> {
176214
// The "call_site" includes the whole macro with its "arguments". We only want
177215
// the macro name.
178216
let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32));
179-
self.matches.insert(new_span, link_from_src);
217+
self.matches.insert(new_span.into(), link_from_src);
180218
true
181219
}
182220

@@ -233,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
233271
intravisit::walk_path(self, path);
234272
}
235273

236-
fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) {
274+
fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
237275
match *qpath {
238276
QPath::TypeRelative(qself, path) => {
239277
if matches!(path.res, Res::Err) {
@@ -249,7 +287,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
249287
self.handle_path(&path, false);
250288
}
251289
} else {
252-
self.infer_id(path.hir_id, Some(id), path.ident.span);
290+
self.infer_id(path.hir_id, Some(id), path.ident.span.into());
253291
}
254292

255293
rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself));
@@ -267,16 +305,18 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
267305
}
268306
}
269307

270-
fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {
308+
fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: rustc_span::Span, id: HirId) {
271309
// To make the difference between "mod foo {}" and "mod foo;". In case we "import" another
272310
// file, we want to link to it. Otherwise no need to create a link.
273311
if !span.overlaps(m.spans.inner_span) {
274312
// Now that we confirmed it's a file import, we want to get the span for the module
275313
// name only and not all the "mod foo;".
276314
if let Node::Item(item) = self.tcx.hir_node(id) {
277315
let (ident, _) = item.expect_mod();
278-
self.matches
279-
.insert(ident.span, LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)));
316+
self.matches.insert(
317+
ident.span.into(),
318+
LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
319+
);
280320
}
281321
} else {
282322
// If it's a "mod foo {}", we want to look to its documentation page.
@@ -288,9 +328,9 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
288328
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
289329
match expr.kind {
290330
ExprKind::MethodCall(segment, ..) => {
291-
self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span)
331+
self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span.into())
292332
}
293-
ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span),
333+
ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span.into()),
294334
_ => {
295335
if self.handle_macro(expr.span) {
296336
// We don't want to go deeper into the macro.

‎src/librustdoc/html/sources.rs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,12 @@ pub(crate) fn print_src(
348348
highlight::write_code(
349349
fmt,
350350
s,
351-
Some(highlight::HrefContext { context, file_span, root_path, current_href }),
351+
Some(highlight::HrefContext {
352+
context,
353+
file_span: file_span.into(),
354+
root_path,
355+
current_href,
356+
}),
352357
Some(decoration_info),
353358
Some(line_info),
354359
);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /