Skip to content

Phase 11.9: combined-counter approach for dealloc-side stats - #62

Merged
jayakasadev merged 1 commit into
mainfrom
feature/phase-11-9-combined-counter
Jun 12, 2026
Merged

jayakasadev merged 1 commit into
mainfrom
feature/phase-11-9-combined-counter

Conversation

@jayakasadev

Copy link
Copy Markdown
Owner

Summary

  • Approach 2 applied (combined-counter for dealloc-side stats): the per-dealloc stats.fast_path_deallocs++ store at the local-owner branch of Allocator::dealloc is removed; stats.fast_path_deallocs += refill_count is pre-credited at the existing Phase 11.8 sites in small_refill / small_refill_slow.
  • Approach 1 (batching at the dealloc message-queue drain) skipped: each local free is independent on the dealloc fast path with no natural batching window, so Approach 2 was applied directly.

Bench results (apples-to-apples 2-run mean on same host)

group 11.8 BASIC 11.9 BASIC verdict
small_allocs 0.9960 1.0006 PASS
medium_allocs 1.0616 1.0611 FAIL
mixed 1.0271 1.0244 FAIL

A separate 5-run BASIC-only sweep showed small_allocs 0.9986, medium_allocs 1.0530, mixed 1.0272.

Acceptance verdict: PARTIAL

  • small_allocs -- PASS (already PASS at 11.8; no regression).
  • medium_allocs -- still FAIL at ~1.06 on this host. Critically, the 11.8 baseline on the same host also sits at 1.062 (the original 1.020 doc figure does not reproduce on the present hardware state). The dealloc store is gone but the bench needle did not move on this group, so the residual is not store-bound -- most likely it is the bytes_in_use atomics on the slab-refill path and/or codegen differences between OFF and BASIC builds.
  • mixed -- still FAIL but improves marginally (1.0271 -> 1.0244) because half of the mixed-size distribution routes through small-class allocs/frees which now pays one fewer store per local free.

The frontend_stats.rs::fast_path_alloc_counter_grows test was adjusted to measure the cumulative dealloc count against the before snapshot (rather than after_alloc), since the credit now lands at slab-grant time before the explicit dealloc loop.

Recommendation

Ship Phase 11.9 as the correct symmetric counterpart to Phase 11.8 (no regressions, marginal mixed improvement, cleaner dealloc hot path), and treat the remaining medium_allocs/mixed gap as fundamental to the BASIC tier on this hardware. Closing it further is a different lever:

  • Introduce a SNMALLOC_STATS_SAMPLED tier (count 1/K allocs+deallocs, multiply at query) -- could approach 1.005.
  • Or relax the 1.02 spec target to 1.06 for medium_allocs / mixed, since the small-class path now meets the strict bar.

Filing a follow-up ticket is not recommended at this point -- batching + combined counters are exhausted as levers.

ClickUp

  • 86aj10b3z

Test plan

  • cmake -B build -DSNMALLOC_STATS_BASIC=ON && cmake --build build -j4 (100% target build)
  • cmake -B build-off -DSNMALLOC_STATS_BASIC=OFF && cmake --build build-off -j4 --target snmallocshim (off-tier build also clean)
  • cargo test --features stats-basic (all 33+ test binaries pass; frontend_stats both cases PASS)
  • cargo bench --features stats-basic --bench stats_bench 5-run sweep + apples-to-apples 2-run A/B vs 11.8 baseline at HEAD

Mirrors the Phase 11.8 batched-counter pattern on the dealloc
side: drop the per-dealloc `stats.fast_path_deallocs++` store at
the local-owner branch of `Allocator::dealloc` and pre-credit
`stats.fast_path_deallocs += refill_count` at slab refill in
`small_refill` / `small_refill_slow`.  Each object placed onto
the fast free list is assumed to be freed locally; cross-thread
frees still bump `remote_deallocs` per-object, so the granting
thread's `fast_path_deallocs` is over-credited by the count of
objects freed by another thread (drift is bounded by program
behaviour and documented on the field).

The `frontend_stats.rs::fast_path_alloc_counter_grows` test now
measures the cumulative dealloc count against the `before`
snapshot rather than `after_alloc`, since the credit lands at
slab-grant time (before the explicit dealloc loop) -- same
end-to-end invariant, just a different measurement window.

Apples-to-apples 2-run mean on the same host vs the 11.8
baseline at HEAD:
  small_allocs:   0.9960 (11.8) -> 1.0006 (11.9), both PASS
  medium_allocs:  1.0616 (11.8) -> 1.0611 (11.9), both FAIL
  mixed:          1.0271 (11.8) -> 1.0244 (11.9), both FAIL

The dealloc store is gone but `medium_allocs` did not close --
the residual ~5-6% on this host is not store-bound; the bench
ratio for medium_allocs is unchanged between 11.8 and 11.9.
Likely candidates are bytes_in_use atomics on the slab refill
path and codegen differences between OFF and BASIC compiles.
Closing that gap requires either a sampled-counter tier or
spec relaxation; tracked in docs/heap-profiling-benchmarks.md
(Phase 11.9 section).
@jayakasadev
jayakasadev merged commit 6a25222 into main Jun 12, 2026
16 of 211 checks passed
@jayakasadev
jayakasadev deleted the feature/phase-11-9-combined-counter branch June 12, 2026 18:53
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