@@ -21,11 +21,28 @@ enum Id {
21
21
None,
22
22
}
23
23
24
- struct NodeData {
24
+ struct NodeStats {
25
25
count: usize,
26
26
size: usize,
27
27
}
28
28
29
+ impl NodeStats {
30
+ fn new() -> NodeStats {
31
+ NodeStats { count: 0, size: 0 }
32
+ }
33
+ }
34
+
35
+ struct Node {
36
+ stats: NodeStats,
37
+ subnodes: FxHashMap<&'static str, NodeStats>,
38
+ }
39
+
40
+ impl Node {
41
+ fn new() -> Node {
42
+ Node { stats: NodeStats::new(), subnodes: FxHashMap::default() }
43
+ }
44
+ }
45
+
29
46
/// This type measures the size of AST and HIR nodes, by implementing the AST
30
47
/// and HIR `Visitor` traits. But we don't measure every visited type because
31
48
/// that could cause double counting.
@@ -45,14 +62,14 @@ struct NodeData {
45
62
/// unfortunate.
46
63
struct StatCollector<'k> {
47
64
krate: Option<Map<'k>>,
48
- data : FxHashMap<&'static str, NodeData >,
65
+ nodes : FxHashMap<&'static str, Node >,
49
66
seen: FxHashSet<Id>,
50
67
}
51
68
52
69
pub fn print_hir_stats(tcx: TyCtxt<'_>) {
53
70
let mut collector = StatCollector {
54
71
krate: Some(tcx.hir()),
55
- data : FxHashMap::default(),
72
+ nodes : FxHashMap::default(),
56
73
seen: FxHashSet::default(),
57
74
};
58
75
tcx.hir().walk_toplevel_module(&mut collector);
@@ -64,47 +81,82 @@ pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
64
81
use rustc_ast::visit::Visitor;
65
82
66
83
let mut collector =
67
- StatCollector { krate: None, data : FxHashMap::default(), seen: FxHashSet::default() };
84
+ StatCollector { krate: None, nodes : FxHashMap::default(), seen: FxHashSet::default() };
68
85
collector.visit_crate(krate);
69
86
collector.print(title);
70
87
}
71
88
72
89
impl<'k> StatCollector<'k> {
73
- fn record<T>(&mut self, label: &'static str, id: Id, node: &T) {
90
+ // Record a top-level node.
91
+ fn record<T>(&mut self, label: &'static str, id: Id, val: &T) {
92
+ self.record_inner(label, None, id, val);
93
+ }
94
+
95
+ // Record a two-level entry, with a top-level enum type and a variant.
96
+ fn record_variant<T>(&mut self, label1: &'static str, label2: &'static str, id: Id, val: &T) {
97
+ self.record_inner(label1, Some(label2), id, val);
98
+ }
99
+
100
+ fn record_inner<T>(
101
+ &mut self,
102
+ label1: &'static str,
103
+ label2: Option<&'static str>,
104
+ id: Id,
105
+ val: &T,
106
+ ) {
74
107
if id != Id::None && !self.seen.insert(id) {
75
108
return;
76
109
}
77
110
78
- let entry = self.data.entry(label).or_insert(NodeData { count: 0, size: 0 });
111
+ let node = self.nodes.entry(label1).or_insert(Node::new());
112
+ node.stats.count += 1;
113
+ node.stats.size = std::mem::size_of_val(val);
79
114
80
- entry.count += 1;
81
- entry.size = std::mem::size_of_val(node);
115
+ if let Some(label2) = label2 {
116
+ let subnode = node.subnodes.entry(label2).or_insert(NodeStats::new());
117
+ subnode.count += 1;
118
+ subnode.size = std::mem::size_of_val(val);
119
+ }
82
120
}
83
121
84
122
fn print(&self, title: &str) {
85
- let mut stats: Vec<_> = self.data.iter().collect();
86
-
87
- stats.sort_by_key(|&(_, ref d)| d.count * d.size);
123
+ let mut nodes: Vec<_> = self.nodes.iter().collect();
124
+ nodes.sort_by_key(|&(_, ref node)| node.stats.count * node.stats.size);
88
125
89
- let total_size = stats .iter().map(|(_, data )| data. count * data .size).sum();
126
+ let total_size = nodes .iter().map(|(_, node )| node.stats. count * node.stats .size).sum();
90
127
91
128
eprintln!("\n{}\n", title);
92
129
93
130
eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size");
94
131
eprintln!("----------------------------------------------------------------");
95
132
96
- let percent = |m, n| { (m * 100) as f64 / n as f64 } ;
133
+ let percent = |m, n| (m * 100) as f64 / n as f64;
97
134
98
- for (label, data ) in stats {
99
- let size = data. count * data .size;
135
+ for (label, node ) in nodes {
136
+ let size = node.stats. count * node.stats .size;
100
137
eprintln!(
101
138
"{:<18}{:>10} ({:4.1}%){:>14}{:>14}",
102
139
label,
103
140
to_readable_str(size),
104
141
percent(size, total_size),
105
- to_readable_str(data .count),
106
- to_readable_str(data .size)
142
+ to_readable_str(node.stats .count),
143
+ to_readable_str(node.stats .size)
107
144
);
145
+ if !node.subnodes.is_empty() {
146
+ let mut subnodes: Vec<_> = node.subnodes.iter().collect();
147
+ subnodes.sort_by_key(|&(_, ref subnode)| subnode.count * subnode.size);
148
+
149
+ for (label, subnode) in subnodes {
150
+ let size = subnode.count * subnode.size;
151
+ eprintln!(
152
+ "- {:<18}{:>10} ({:4.1}%){:>14}",
153
+ label,
154
+ to_readable_str(size),
155
+ percent(size, total_size),
156
+ to_readable_str(subnode.count),
157
+ );
158
+ }
159
+ }
108
160
}
109
161
eprintln!("----------------------------------------------------------------");
110
162
eprintln!("{:<18}{:>10}\n", "Total", to_readable_str(total_size));
@@ -268,14 +320,54 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
268
320
}
269
321
}
270
322
323
+ // Used to avoid boilerplate for types with many variants.
324
+ macro_rules! record_variants {
325
+ (
326
+ ($self:ident, $val:expr, $kind:expr, $ty:ty, $tykind:ident), // mandatory pieces
327
+ [$($variant:ident),*]
328
+ ) => {
329
+ match $kind {
330
+ $(
331
+ ast::$tykind::$variant { .. } => {
332
+ $self.record_variant(stringify!($ty), stringify!($variant), Id::None, $val)
333
+ }
334
+ )*
335
+ }
336
+ };
337
+ }
338
+
271
339
impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
272
340
fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
273
- self.record("ForeignItem", Id::None, i);
341
+ record_variants!(
342
+ (self, i, i.kind, ForeignItem, ForeignItemKind),
343
+ [Static, Fn, TyAlias, MacCall]
344
+ );
274
345
ast_visit::walk_foreign_item(self, i)
275
346
}
276
347
277
348
fn visit_item(&mut self, i: &'v ast::Item) {
278
- self.record("Item", Id::None, i);
349
+ record_variants!(
350
+ (self, i, i.kind, Item, ItemKind),
351
+ [
352
+ ExternCrate,
353
+ Use,
354
+ Static,
355
+ Const,
356
+ Fn,
357
+ Mod,
358
+ ForeignMod,
359
+ GlobalAsm,
360
+ TyAlias,
361
+ Enum,
362
+ Struct,
363
+ Union,
364
+ Trait,
365
+ TraitAlias,
366
+ Impl,
367
+ MacCall,
368
+ MacroDef
369
+ ]
370
+ );
279
371
ast_visit::walk_item(self, i)
280
372
}
281
373
@@ -290,7 +382,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
290
382
}
291
383
292
384
fn visit_stmt(&mut self, s: &'v ast::Stmt) {
293
- self.record("Stmt", Id::None, s);
385
+ record_variants!(
386
+ (self, s, s.kind, Stmt, StmtKind),
387
+ [Local, Item, Expr, Semi, Empty, MacCall]
388
+ );
294
389
ast_visit::walk_stmt(self, s)
295
390
}
296
391
@@ -305,17 +400,66 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
305
400
}
306
401
307
402
fn visit_pat(&mut self, p: &'v ast::Pat) {
308
- self.record("Pat", Id::None, p);
403
+ record_variants!(
404
+ (self, p, p.kind, Pat, PatKind),
405
+ [
406
+ Wild,
407
+ Ident,
408
+ Struct,
409
+ TupleStruct,
410
+ Or,
411
+ Path,
412
+ Tuple,
413
+ Box,
414
+ Ref,
415
+ Lit,
416
+ Range,
417
+ Slice,
418
+ Rest,
419
+ Paren,
420
+ MacCall
421
+ ]
422
+ );
309
423
ast_visit::walk_pat(self, p)
310
424
}
311
425
312
- fn visit_expr(&mut self, ex: &'v ast::Expr) {
313
- self.record("Expr", Id::None, ex);
314
- ast_visit::walk_expr(self, ex)
426
+ fn visit_expr(&mut self, e: &'v ast::Expr) {
427
+ record_variants!(
428
+ (self, e, e.kind, Expr, ExprKind),
429
+ [
430
+ Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
431
+ If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
432
+ AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
433
+ InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err
434
+ ]
435
+ );
436
+ ast_visit::walk_expr(self, e)
315
437
}
316
438
317
439
fn visit_ty(&mut self, t: &'v ast::Ty) {
318
- self.record("Ty", Id::None, t);
440
+ record_variants!(
441
+ (self, t, t.kind, Ty, TyKind),
442
+ [
443
+ Slice,
444
+ Array,
445
+ Ptr,
446
+ Rptr,
447
+ BareFn,
448
+ Never,
449
+ Tup,
450
+ Path,
451
+ TraitObject,
452
+ ImplTrait,
453
+ Paren,
454
+ Typeof,
455
+ Infer,
456
+ ImplicitSelf,
457
+ MacCall,
458
+ Err,
459
+ CVarArgs
460
+ ]
461
+ );
462
+
319
463
ast_visit::walk_ty(self, t)
320
464
}
321
465
@@ -325,7 +469,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
325
469
}
326
470
327
471
fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
328
- self.record("WherePredicate", Id::None, p);
472
+ record_variants!(
473
+ (self, p, p, WherePredicate, WherePredicate),
474
+ [BoundPredicate, RegionPredicate, EqPredicate]
475
+ );
329
476
ast_visit::walk_where_predicate(self, p)
330
477
}
331
478
@@ -334,14 +481,17 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
334
481
ast_visit::walk_fn(self, fk, s)
335
482
}
336
483
337
- fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
338
- self.record("AssocItem", Id::None, item);
339
- ast_visit::walk_assoc_item(self, item, ctxt);
484
+ fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
485
+ record_variants!(
486
+ (self, i, i.kind, AssocItem, AssocItemKind),
487
+ [Const, Fn, TyAlias, MacCall]
488
+ );
489
+ ast_visit::walk_assoc_item(self, i, ctxt);
340
490
}
341
491
342
- fn visit_param_bound(&mut self, bounds : &'v ast::GenericBound, _ctxt: BoundKind) {
343
- self.record(" GenericBound", Id::None, bounds );
344
- ast_visit::walk_param_bound(self, bounds )
492
+ fn visit_param_bound(&mut self, b : &'v ast::GenericBound, _ctxt: BoundKind) {
493
+ record_variants!((self, b, b, GenericBound, GenericBound), [Trait, Outlives] );
494
+ ast_visit::walk_param_bound(self, b )
345
495
}
346
496
347
497
fn visit_field_def(&mut self, s: &'v ast::FieldDef) {
@@ -369,12 +519,12 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
369
519
// common, so we implement `visit_generic_args` and tolerate the double
370
520
// counting in the former case.
371
521
fn visit_generic_args(&mut self, sp: Span, g: &'v ast::GenericArgs) {
372
- self.record("GenericArgs", Id::None , g);
522
+ record_variants!((self, g , g, GenericArgs, GenericArgs), [AngleBracketed, Parenthesized] );
373
523
ast_visit::walk_generic_args(self, sp, g)
374
524
}
375
525
376
526
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
377
- self.record(" Attribute", Id::None, attr );
527
+ record_variants!(( self, attr, attr.kind, Attribute, AttrKind), [Normal, DocComment] );
378
528
ast_visit::walk_attribute(self, attr)
379
529
}
380
530
0 commit comments