Skip to content

feat(profile): cache resolved symbols across symbolize() calls (CU-86aj3uw04) - #79

Merged
jayakasadev merged 1 commit into
mainfrom
feature/symbol-cache
Jun 17, 2026
Merged

feat(profile): cache resolved symbols across symbolize() calls (CU-86aj3uw04)#79
jayakasadev merged 1 commit into
mainfrom
feature/symbol-cache

Conversation

@jayakasadev

Copy link
Copy Markdown
Owner

What

Memoize resolve_one(addr) behind a process-global OnceLock<Arc<Mutex<HashMap<usize, ResolvedFrame>>>>. HeapProfile::symbolize now hits the cache instead of re-running backtrace::resolve on every call. New clear_symbol_cache() flushes contents without dropping the cell.

Why

Heap-profile-eval against :konfig_bin_heapprof (CU-86aj360ae, 2026-06-16) measured ~17 MB transient Vec + ~20 ms self-CPU per /debug/heap-profile.pprof scrape, rooted at backtrace::symbolize::gimli::macho::Object::parse → Vec::reserve → __rust_realloc. Code addresses are stable within a process — first scrape pays parse cost, subsequent scrapes are free.

CU-86aj35zev confirmed cost is intrinsic to the snmalloc-rs symbolicate path, not a downstream transitive.

Cost

  • Cold path identical to today.
  • Warm path: one Mutex lock + HashMap lookup per unique frame.
  • Memory: 1 entry per unique resolved frame (≈518 kB long-lived for konfig — matches the per-scrape live-set today, now amortised).

Evidence

  • cargo test --features profiling,symbolicate --lib → 42/42 pass.
  • cargo build --features profiling (no symbolicate) → clean.
  • New symbol_cache_cell_stable_across_pprof_writes: Arc-ptr equality across two write_pprof_gz calls + after clear_symbol_cache.
  • New symbol_cache_returns_equal_frame_on_second_call: cached lookup matches fresh resolve.
  • Existing symbolize_resolves_known_function_name still green.

Process-global, lazily-initialised `HashMap<usize, ResolvedFrame>`
guarded by `OnceLock<Arc<Mutex<...>>>` memoizes `resolve_one`. First
`HeapProfile::symbolize` pays the backtrace/gimli parse cost; later
calls are a cache lookup per unique frame address.

Motivation (CU-86aj3uw04): heap-profile-eval against
:konfig_bin_heapprof showed ~17 MB transient Vec + ~20 ms self-CPU per
/debug/heap-profile.pprof scrape, rooted at
`backtrace::symbolize::gimli::macho::Object::parse`. Code addresses
are stable for a process lifetime, so the answer is too.

Surface:
  pub fn clear_symbol_cache();  // flush, keep cell

Tests:
  symbol_cache_cell_stable_across_pprof_writes  Arc-ptr equality
  symbol_cache_returns_equal_frame_on_second_call  value equality
@jayakasadev
jayakasadev merged commit 185c7a1 into main Jun 17, 2026
68 of 119 checks passed
@jayakasadev
jayakasadev deleted the feature/symbol-cache branch June 17, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant