Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/v1/stage0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,10 @@ path = "src/bin/v1_src_dag_parse.rs"
[[bin]]
name = "wiring_liveness_whole_tree"
path = "src/bin/wiring_liveness_whole_tree.rs"

# Measurement binary: walks MultiEntryIndex caches after a resolve and reports
# shared-vs-distinct Rc<> pointer stats for typed_module_cache, parse_cache.
# Used to diagnose ~4 GiB retention root cause before cutting a fix.
[[bin]]
name = "cache_walk"
path = "src/bin/cache_walk.rs"
72 changes: 72 additions & 0 deletions src/v1/stage0/src/bin/cache_walk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SCAFFOLD: diagnostic bin for typed_module_cache retention measurement; delete once func_env.sigs
// Rc-sharing dedup lands. Dissolution trigger = func_env.sig sharing > 1.0× in corpus.
#![allow(clippy::disallowed_macros)]

use std::process::ExitCode;

use v1_compiler::cli_run::{build_multi_entry_index, resolve_entry_with_index};

fn usage() -> ! {
eprintln!("cache_walk: measure MultiEntryIndex cache structure after resolving entries");
eprintln!(
"usage: cache_walk --source-root <dir> [--source-root <dir>...] --entry <file> [--entry <file>...]"
);
std::process::exit(1);
}

fn main() -> ExitCode {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut source_roots: Vec<String> = Vec::new();
let mut entries: Vec<String> = Vec::new();
let mut i = 0;
while i < args.len() {
match args[i].as_str() {
"--source-root" => {
i += 1;
if i >= args.len() {
usage();
}
source_roots.push(args[i].clone());
}
"--entry" => {
i += 1;
if i >= args.len() {
usage();
}
entries.push(args[i].clone());
}
_ => {
eprintln!("unknown arg: {}", args[i]);
usage();
}
}
i += 1;
}
if source_roots.is_empty() || entries.is_empty() {
usage();
}

eprintln!("cache_walk: building index for {:?}", source_roots);
let index = build_multi_entry_index(&source_roots);

for entry in &entries {
eprintln!("cache_walk: resolving entry {entry}");
match resolve_entry_with_index(&index, entry) {
Err(e) => {
eprintln!("cache_walk: resolve failed for {entry}: {e}");
return ExitCode::FAILURE;
}
Ok(_) => {
eprintln!(
"cache_walk: resolve OK ({} modules cached so far)",
index.typed_module_count()
);
}
}
}

let report = index.measure_caches();
println!("{}", report.format());

ExitCode::SUCCESS
}
176 changes: 176 additions & 0 deletions src/v1/stage0/src/cli_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,182 @@ pub struct MultiEntryIndex {
typed_module_cache: RefCell<HashMap<String, Rc<v1_compiler_infer::TypecheckModuleResult>>>,
}

// SCAFFOLD: delete once func_env.sigs Rc-sharing dedup lands; dissolution trigger = func_env.sig sharing > 1.0× in corpus.
pub struct CacheWalkReport {
pub typed_module_count: usize,
pub parse_cache_entry_count: usize,
// typed_module_cache: TypeEnv.bindings
pub total_bindings: usize,
pub distinct_binding_ptrs: usize,
// typed_module_cache: TypeEnv.source_indices
pub total_source_index_entries: usize,
pub distinct_source_index_ptrs: usize,
// typed_module_cache: TypeEnv.inductive_fields (map entry count)
pub total_inductive_field_entries: usize,
pub distinct_inductive_field_vec_ptrs: usize,
// typed_module_cache: TypeEnv.intern_table
pub distinct_intern_table_ptrs: usize,
// typed_module_cache: func_env.signatures
pub total_func_sigs: usize,
pub distinct_func_sig_ptrs: usize,
// typed_module_cache: typed items (Rc<Node> in items vec)
pub total_typed_items: usize,
pub distinct_typed_item_node_ptrs: usize,
// parse_cache: parsed module nodes (Rc<Node>)
pub total_parse_module_nodes: usize,
pub distinct_parse_module_node_ptrs: usize,
}

impl CacheWalkReport {
pub fn format(&self) -> String {
let sharing = |total: usize, distinct: usize| -> String {
if distinct == 0 {
return "n/a".to_string();
}
format!("{:.1}×", total as f64 / distinct as f64)
};
format!(
"=== MultiEntryIndex cache walk ===\n\
typed_module_cache: {} modules\n\
parse_cache: {} files\n\
\n\
TypeEnv.bindings:\n\
total entries: {}\n\
distinct Rc<>: {} (sharing {}, ideal 1×)\n\
TypeEnv.source_indices entries:\n\
total: {}\n\
distinct Rc<NewlineIndex>: {} (sharing {})\n\
TypeEnv.inductive_fields entries:\n\
total: {}\n\
distinct Rc<Vec<>>: {} (sharing {})\n\
TypeEnv.intern_table:\n\
distinct Rc<>: {} (sharing {})\n\
func_env.signatures:\n\
total: {}\n\
distinct Rc<>: {} (sharing {})\n\
typed items (Rc<Node> in items):\n\
total: {}\n\
distinct Rc<>: {} (sharing {})\n\
parse module nodes:\n\
total: {}\n\
distinct Rc<>: {} (sharing {})\n",
self.typed_module_count,
self.parse_cache_entry_count,
self.total_bindings,
self.distinct_binding_ptrs,
sharing(self.total_bindings, self.distinct_binding_ptrs),
self.total_source_index_entries,
self.distinct_source_index_ptrs,
sharing(
self.total_source_index_entries,
self.distinct_source_index_ptrs
),
self.total_inductive_field_entries,
self.distinct_inductive_field_vec_ptrs,
sharing(
self.total_inductive_field_entries,
self.distinct_inductive_field_vec_ptrs
),
self.distinct_intern_table_ptrs,
sharing(self.typed_module_count, self.distinct_intern_table_ptrs),
self.total_func_sigs,
self.distinct_func_sig_ptrs,
sharing(self.total_func_sigs, self.distinct_func_sig_ptrs),
self.total_typed_items,
self.distinct_typed_item_node_ptrs,
sharing(self.total_typed_items, self.distinct_typed_item_node_ptrs),
self.total_parse_module_nodes,
self.distinct_parse_module_node_ptrs,
sharing(
self.total_parse_module_nodes,
self.distinct_parse_module_node_ptrs
),
)
}
}

impl MultiEntryIndex {
pub fn typed_module_count(&self) -> usize {
self.typed_module_cache.borrow().len()
}

pub fn measure_caches(&self) -> CacheWalkReport {
use crate::v1_compiler_infer_env::TypeEnv;
use std::collections::HashSet;

let typed_cache = self.typed_module_cache.borrow();
let parse_cache_ref = self.parse_cache.borrow();

let mut total_bindings = 0usize;
let mut binding_ptrs: HashSet<*const crate::v1_compiler_infer_env::TypeBinding> =
HashSet::new();
let mut total_si_entries = 0usize;
let mut si_ptrs: HashSet<*const NewlineIndex> = HashSet::new();
let mut total_if_entries = 0usize;
let mut if_vec_ptrs: HashSet<usize> = HashSet::new();
let mut intern_ptrs: HashSet<*const InternTable> = HashSet::new();
let mut total_func_sigs = 0usize;
let mut sig_ptrs: HashSet<*const crate::v1_compiler_infer_sigs::ResolvedFuncSig> =
HashSet::new();
let mut total_typed_items = 0usize;
let mut typed_item_ptrs: HashSet<*const Node> = HashSet::new();

for tc_result in typed_cache.values() {
let tm = &tc_result.typed;
let env = &tm.type_env;

for binding in env.bindings.values() {
total_bindings += 1;
binding_ptrs.insert(Rc::as_ptr(binding));
}
for si in env.source_indices.values() {
total_si_entries += 1;
si_ptrs.insert(Rc::as_ptr(si));
}
for iv in env.inductive_fields.values() {
total_if_entries += 1;
if_vec_ptrs.insert(Rc::as_ptr(iv) as usize);
}
intern_ptrs.insert(Rc::as_ptr(&env.intern_table));
for sig in tm.func_env.signatures.values() {
total_func_sigs += 1;
sig_ptrs.insert(Rc::as_ptr(sig));
}
for item in tm.items.iter() {
total_typed_items += 1;
typed_item_ptrs.insert(Rc::as_ptr(item));
}
}

let mut total_parse_nodes = 0usize;
let mut parse_node_ptrs: HashSet<*const Node> = HashSet::new();
for (parse_result, _) in parse_cache_ref.values() {
if let Some(module) = &parse_result.module {
total_parse_nodes += 1;
parse_node_ptrs.insert(Rc::as_ptr(module));
}
}

CacheWalkReport {
typed_module_count: typed_cache.len(),
parse_cache_entry_count: parse_cache_ref.len(),
total_bindings,
distinct_binding_ptrs: binding_ptrs.len(),
total_source_index_entries: total_si_entries,
distinct_source_index_ptrs: si_ptrs.len(),
total_inductive_field_entries: total_if_entries,
distinct_inductive_field_vec_ptrs: if_vec_ptrs.len(),
distinct_intern_table_ptrs: intern_ptrs.len(),
total_func_sigs,
distinct_func_sig_ptrs: sig_ptrs.len(),
total_typed_items,
distinct_typed_item_node_ptrs: typed_item_ptrs.len(),
total_parse_module_nodes: total_parse_nodes,
distinct_parse_module_node_ptrs: parse_node_ptrs.len(),
}
}
}

fn seed_kernel_intern_names(table: Rc<InternTable>) -> Rc<InternTable> {
let mut t = table;
for name in v1_rt::map_keys(&kernel_type_set()).iter().cloned() {
Expand Down