Skip to content

Commit 217693a

Browse files
committed
Auto merge of #138927 - nnethercote:rearrange-Item-ItemInner, r=GuillaumeGomez
rustdoc: Rearrange `Item`/`ItemInner`. The `Item` struct is 48 bytes and contains a `Box<ItemInner>`; `ItemInner` is 104 bytes. This is an odd arrangement. Normally you'd have one of the following. - A single large struct, which avoids the allocation for the `Box`, but can result in lots of wasted space in unused parts of a container like `Vec<Item>`, `HashSet<Item>`, etc. - Or, something like `struct Item(Box<ItemInner>)`, which requires the `Box` allocation but gives a very small Item size, which is good for containers like `Vec<Item>`. `Item`/`ItemInner` currently gets the worst of both worlds: it always requires a `Box`, but `Item` is also pretty big and so wastes space in containers. It would make sense to push it in one direction or the other. #138916 showed that the first option is a regression for rustdoc, so this commit does the second option, which improves speed and reduces memory usage. r? `@GuillaumeGomez`
2 parents ecb170a + ffee55c commit 217693a

File tree

8 files changed

+46
-37
lines changed

8 files changed

+46
-37
lines changed

src/librustdoc/clean/auto_trait.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ fn synthesize_auto_trait_impl<'tcx>(
114114
};
115115

116116
Some(clean::Item {
117-
name: None,
118117
inner: Box::new(clean::ItemInner {
118+
name: None,
119119
attrs: Default::default(),
120120
stability: None,
121121
kind: clean::ImplItem(Box::new(clean::Impl {
@@ -127,10 +127,10 @@ fn synthesize_auto_trait_impl<'tcx>(
127127
polarity,
128128
kind: clean::ImplKind::Auto,
129129
})),
130+
item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
131+
cfg: None,
132+
inline_stmt_id: None,
130133
}),
131-
item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
132-
cfg: None,
133-
inline_stmt_id: None,
134134
})
135135
}
136136

src/librustdoc/clean/blanket_impl.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ pub(crate) fn synthesize_blanket_impls(
8383
cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id));
8484

8585
blanket_impls.push(clean::Item {
86-
name: None,
87-
item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
8886
inner: Box::new(clean::ItemInner {
87+
name: None,
88+
item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
8989
attrs: Default::default(),
9090
stability: None,
9191
kind: clean::ImplItem(Box::new(clean::Impl {
@@ -122,9 +122,9 @@ pub(crate) fn synthesize_blanket_impls(
122122
None,
123123
))),
124124
})),
125+
cfg: None,
126+
inline_stmt_id: None,
125127
}),
126-
cfg: None,
127-
inline_stmt_id: None,
128128
});
129129
}
130130
}

src/librustdoc/clean/inline.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ pub(crate) fn try_inline(
151151
let mut item =
152152
crate::clean::generate_item_with_correct_attrs(cx, kind, did, name, import_def_id, None);
153153
// The visibility needs to reflect the one from the reexport and not from the "source" DefId.
154-
item.inline_stmt_id = import_def_id;
154+
item.inner.inline_stmt_id = import_def_id;
155155
ret.push(item);
156156
Some(ret)
157157
}
@@ -655,11 +655,11 @@ fn build_module_items(
655655
// Primitive types can't be inlined so generate an import instead.
656656
let prim_ty = clean::PrimitiveType::from(p);
657657
items.push(clean::Item {
658-
name: None,
659-
// We can use the item's `DefId` directly since the only information ever used
660-
// from it is `DefId.krate`.
661-
item_id: ItemId::DefId(did),
662658
inner: Box::new(clean::ItemInner {
659+
name: None,
660+
// We can use the item's `DefId` directly since the only information ever
661+
// used from it is `DefId.krate`.
662+
item_id: ItemId::DefId(did),
663663
attrs: Default::default(),
664664
stability: None,
665665
kind: clean::ImportItem(clean::Import::new_simple(
@@ -679,9 +679,9 @@ fn build_module_items(
679679
},
680680
true,
681681
)),
682+
cfg: None,
683+
inline_stmt_id: None,
682684
}),
683-
cfg: None,
684-
inline_stmt_id: None,
685685
});
686686
} else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) {
687687
items.extend(i)

src/librustdoc/clean/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ fn generate_item_with_correct_attrs(
210210

211211
let name = renamed.or(Some(name));
212212
let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg);
213-
item.inline_stmt_id = import_id;
213+
item.inner.inline_stmt_id = import_id;
214214
item
215215
}
216216

src/librustdoc/clean/types.rs

+26-16
Original file line numberDiff line numberDiff line change
@@ -311,26 +311,31 @@ pub(crate) enum ExternalLocation {
311311
/// directly to the AST's concept of an item; it's a strict superset.
312312
#[derive(Clone)]
313313
pub(crate) struct Item {
314-
/// The name of this item.
315-
/// Optional because not every item has a name, e.g. impls.
316-
pub(crate) name: Option<Symbol>,
317314
pub(crate) inner: Box<ItemInner>,
318-
pub(crate) item_id: ItemId,
319-
/// This is the `LocalDefId` of the `use` statement if the item was inlined.
320-
/// The crate metadata doesn't hold this information, so the `use` statement
321-
/// always belongs to the current crate.
322-
pub(crate) inline_stmt_id: Option<LocalDefId>,
323-
pub(crate) cfg: Option<Arc<Cfg>>,
324315
}
325316

317+
// Why does the `Item`/`ItemInner` split exist? `Vec<Item>`s are common, and
318+
// without the split `Item` would be a large type (100+ bytes) which results in
319+
// lots of wasted space in the unused parts of a `Vec<Item>`. With the split,
320+
// `Item` is just 8 bytes, and the wasted space is avoided, at the cost of an
321+
// extra allocation per item. This is a performance win.
326322
#[derive(Clone)]
327323
pub(crate) struct ItemInner {
324+
/// The name of this item.
325+
/// Optional because not every item has a name, e.g. impls.
326+
pub(crate) name: Option<Symbol>,
328327
/// Information about this item that is specific to what kind of item it is.
329328
/// E.g., struct vs enum vs function.
330329
pub(crate) kind: ItemKind,
331330
pub(crate) attrs: Attributes,
332331
/// The effective stability, filled out by the `propagate-stability` pass.
333332
pub(crate) stability: Option<Stability>,
333+
pub(crate) item_id: ItemId,
334+
/// This is the `LocalDefId` of the `use` statement if the item was inlined.
335+
/// The crate metadata doesn't hold this information, so the `use` statement
336+
/// always belongs to the current crate.
337+
pub(crate) inline_stmt_id: Option<LocalDefId>,
338+
pub(crate) cfg: Option<Arc<Cfg>>,
334339
}
335340

336341
impl std::ops::Deref for Item {
@@ -488,11 +493,15 @@ impl Item {
488493
trace!("name={name:?}, def_id={def_id:?} cfg={cfg:?}");
489494

490495
Item {
491-
item_id: def_id.into(),
492-
inner: Box::new(ItemInner { kind, attrs, stability: None }),
493-
name,
494-
cfg,
495-
inline_stmt_id: None,
496+
inner: Box::new(ItemInner {
497+
item_id: def_id.into(),
498+
kind,
499+
attrs,
500+
stability: None,
501+
name,
502+
cfg,
503+
inline_stmt_id: None,
504+
}),
496505
}
497506
}
498507

@@ -2622,13 +2631,14 @@ mod size_asserts {
26222631

26232632
use super::*;
26242633
// tidy-alphabetical-start
2625-
static_assert_size!(Crate, 56); // frequently moved by-value
2634+
static_assert_size!(Crate, 16); // frequently moved by-value
26262635
static_assert_size!(DocFragment, 32);
26272636
static_assert_size!(GenericArg, 32);
26282637
static_assert_size!(GenericArgs, 24);
26292638
static_assert_size!(GenericParamDef, 40);
26302639
static_assert_size!(Generics, 16);
2631-
static_assert_size!(Item, 48);
2640+
static_assert_size!(Item, 8);
2641+
static_assert_size!(ItemInner, 136);
26322642
static_assert_size!(ItemKind, 48);
26332643
static_assert_size!(PathSegment, 32);
26342644
static_assert_size!(Type, 32);

src/librustdoc/formats/cache.rs

-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,6 @@ impl DocFolder for CacheBuilder<'_, '_> {
385385
// implementations elsewhere.
386386
let ret = if let clean::Item {
387387
inner: box clean::ItemInner { kind: clean::ImplItem(ref i), .. },
388-
..
389388
} = item
390389
{
391390
// Figure out the id of this impl. This may map to a

src/librustdoc/json/conversions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl JsonRenderer<'_> {
4343
let attrs = item.attributes(self.tcx, self.cache(), true);
4444
let span = item.span(self.tcx);
4545
let visibility = item.visibility(self.tcx);
46-
let clean::Item { name, item_id, .. } = item;
46+
let clean::ItemInner { name, item_id, .. } = *item.inner;
4747
let id = self.id_from_item(&item);
4848
let inner = match item.kind {
4949
clean::KeywordItem => return None,

src/librustdoc/passes/propagate_doc_cfg.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl CfgPropagator<'_, '_> {
6161

6262
let (_, cfg) =
6363
merge_attrs(self.cx, item.attrs.other_attrs.as_slice(), Some((&attrs, None)));
64-
item.cfg = cfg;
64+
item.inner.cfg = cfg;
6565
}
6666
}
6767

@@ -71,7 +71,7 @@ impl DocFolder for CfgPropagator<'_, '_> {
7171

7272
self.merge_with_parent_attributes(&mut item);
7373

74-
let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
74+
let new_cfg = match (self.parent_cfg.take(), item.inner.cfg.take()) {
7575
(None, None) => None,
7676
(Some(rc), None) | (None, Some(rc)) => Some(rc),
7777
(Some(mut a), Some(b)) => {
@@ -81,7 +81,7 @@ impl DocFolder for CfgPropagator<'_, '_> {
8181
}
8282
};
8383
self.parent_cfg = new_cfg.clone();
84-
item.cfg = new_cfg;
84+
item.inner.cfg = new_cfg;
8585

8686
let old_parent =
8787
if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) {

0 commit comments

Comments
 (0)