forge/profile/analyze/
observations.rs1use super::{symbols::clean_name, types::Function};
2
3const MAX_BOTTLENECK_OBS: usize = 3;
4const OBS_ALLOC_HEAVY_PCT: f64 = 25.0;
5const OBS_INIT_HEAVY_PCT: f64 = 15.0;
6const OBS_ROTATION_HEAVY_PCT: f64 = 20.0;
7const OBS_DATA_LOADING_PCT: f64 = 5.0;
8const OBS_OTHER_PCT: f64 = 15.0;
9const OBS_BOTTLENECK_TOTAL_PCT: f64 = 20.0;
10const OBS_BOTTLENECK_SELF_PCT: f64 = 2.0;
11
12pub(super) fn generate_observations(
14 categories: &[(String, f64)],
15 functions: &[Function],
16 bottlenecks: &[Function],
17) -> Vec<String> {
18 let mut obs = Vec::new();
19 let cat_pct = |name: &str| -> f64 {
20 categories
21 .iter()
22 .find(|(n, _)| n == name)
23 .map_or(0.0, |(_, p)| *p)
24 };
25
26 let mem_pct = cat_pct("memory") + cat_pct("collections");
27
28 if mem_pct > OBS_ALLOC_HEAVY_PCT {
29 obs.push(format!(
30 "ALLOCATION HEAVY: {mem_pct:.0}% of time in memory/collections. \
31 Look for Vec resizing, HashMap rehashing, excessive clone/drop."
32 ));
33 }
34
35 let init_pct: f64 = functions
36 .iter()
37 .filter(|f| {
38 ["::new", "::with_", "::init", "::summon", "::build"]
39 .iter()
40 .any(|p| f.name.contains(p))
41 })
42 .map(|f| f.self_pct)
43 .sum();
44
45 if init_pct > OBS_INIT_HEAVY_PCT {
46 obs.push(format!(
47 "INIT HEAVY: {init_pct:.0}% of self-time in constructors/init functions. \
48 Consider object pooling or lazy initialization across iterations."
49 ));
50 }
51
52 let rotation_pct = cat_pct("rotation");
53
54 if rotation_pct > OBS_ROTATION_HEAVY_PCT {
55 obs.push(format!(
56 "ROTATION EVAL: {rotation_pct:.0}% in rotation evaluation. \
57 Check condition complexity, JIT codegen quality, descriptor evaluation."
58 ));
59 }
60
61 let data_pct = cat_pct("data");
62
63 if data_pct > OBS_DATA_LOADING_PCT {
64 obs.push(format!(
65 "DATA LOADING: {data_pct:.0}% in data loading (resolver). \
66 This should be near 0% for large iteration counts. \
67 Data may be re-loaded per sim instead of shared."
68 ));
69 }
70
71 let other_pct = cat_pct("other");
72
73 if other_pct > OBS_OTHER_PCT {
74 obs.push(format!(
75 "UNCLASSIFIED: {other_pct:.0}% in 'other' category. \
76 Functions not matching any engine subsystem pattern. \
77 May need new category rules or better symbol resolution."
78 ));
79 }
80
81 for b in bottlenecks.iter().take(MAX_BOTTLENECK_OBS) {
82 if b.total_pct >= OBS_BOTTLENECK_TOTAL_PCT && b.self_pct < OBS_BOTTLENECK_SELF_PCT {
83 let name = clean_name(&b.name);
84
85 obs.push(format!(
86 "BOTTLENECK: {name} has {:.1}% total but only {:.1}% self. \
87 It orchestrates hot code -- optimizing its callees has outsized impact.",
88 b.total_pct, b.self_pct
89 ));
90 }
91 }
92
93 obs
94}