Skip to content
Closed
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
1 change: 1 addition & 0 deletions changelog.d/alloc-census-rust-heap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat(runtime): `PERRY_ALLOC_CENSUS` — a sampling profiler for the Rust heap, behind the off-by-default `alloc-census` feature. The GC census accounts for the arena and the side tables; on the compiled claude-code TUI those two explain ~115 MB of a 300 MB idle footprint and ~430 MB of a 2 GB peak, and nothing in the runtime could say where the rest came from. The census wraps the `#[global_allocator]` and reports exact totals plus a power-of-two size-class histogram, and samples one call site per MiB allocated (raw `backtrace(3)` frames, symbolised offline with `atos`), subtracting a sampled pointer again when it is freed — so it reports *live* native bytes per call site, not just churn. Measured on a 400-character reply: **22.5 GB allocated in 31.7 M calls, peak live 1.70 GB**, of which the largest single owners are the GC's own side-table scanners rebuilding their hash maps inside every copying minor (`descriptor_state::scan_descriptor_roots_mut` 222 MB, `shapes::scan_shape_table_rekey_mut` 75 MB, `restore_surviving_dirty_coverage` 29 MB) and regex program construction (~127 MB). Dumped with the heap census on `SIGUSR2`, alongside `mi_stats_print`.
8 changes: 8 additions & 0 deletions crates/perry-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ keepalive-anchors = []
# leaves it off and falls back to the system allocator. The #6882 VM-tag
# retag rides the same gate (it only exists to label mimalloc's mappings).
alloc-mimalloc = ["dep:mimalloc", "dep:libmimalloc-sys"]

# `PERRY_ALLOC_CENSUS` — a sampling profiler for the Rust heap
# (`alloc_census`). OFF by default and compiled out entirely: the census wraps
# the `#[global_allocator]`, and `gc_malloc` runs ~1M times/sec, so even the
# one relaxed load its disabled state costs has no business in a shipped
# build. Enable it to attribute native-heap bytes to call sites; see the
# module docs.
alloc-census = []
# DIAGNOSTIC ONLY — never in `default`, never in a shipping build.
#
# Builds mimalloc in secure + debug mode to hunt heap corruption that is
Expand Down
Loading
Loading