Repository navigation
More robust memory reporting for the Ractor harness - #548
Merged
Merged
Conversation
Scenario benchmarks record retained and peak RSS, but run_benchmarks.rb only kept them in the JSON. In this commit we add a Scenario memory table below the GC tables, with one row per Ractor count. It shows the median and mean retained RSS, and the highest peak any trial reached. The harness now records the mean and max of each metric next to the median, in whole bytes. Co-authored-by: eightbitraptor <matt@eightbitraptor.com>
Off Linux, get_rss shells out to ps, which forks on every call, and the scenario peak sampler calls it every 5ms. A procps ps from Nix earlier in PATH fails with "requires entitlement", so get_rss fell back to the lifetime peak RSS. In this commit macOS reads ri_resident_size from proc_pid_rusage through Fiddle, and falls back to /bin/ps before the ps in PATH. Co-authored-by: eightbitraptor <matt@eightbitraptor.com>
The scenario benchmarks took each worker's value in spawn order, so a worker that finished early stayed alive until every worker ahead of it had finished. In this commit they use Ractor.select to take whichever worker finishes next. Co-authored-by: eightbitraptor <matt@eightbitraptor.com>
glibc's free() only returns memory to the OS from the top of the heap, so memory Ruby has freed (Ractor message copies, for example) stays resident while a live chunk sits above it. Ruby's GC doesn't trim, so retained RSS included glibc's caching. In this commit gc_settle calls malloc_trim(0) after the full GCs. Where libc has no malloc_trim the harness prints why, and ractor_mem_malloc_trim in the JSON records whether libc has it. Co-authored-by: eightbitraptor <matt@eightbitraptor.com>
gc_settle runs two full GCs, a malloc_trim and a sleep around every scenario trial, and they swamp a profile of the scenario itself. In this commit RUBY_BENCH_PROFILING=1 skips it. Retained RSS then includes uncollected garbage. Co-authored-by: eightbitraptor <matt@eightbitraptor.com>
The README showed --ractor-gc only for the whole ractor category, and didn't say that run_benchmarks.rb skips Ractor harness benchmarks without --category ractor. In this commit we add an example that compares two Rubies on one scenario benchmark, the direct-run form with RUBY_BENCH_RACTOR_GC=1, and a section on profiling a benchmark with samply.
eightbitraptor
requested review from
luke-gru and
luke-gruber
and removed request for
luke-gru
October 9, 2026 18:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A follow-up to the Ractor scenario benchmarks, mostly about making the memory numbers easier to read and more trustworthy.
Scenario memory table.
run_benchmarks.rbnow prints the retained and peak RSS of scenario benchmarks below the GC tables, with one row per Ractor count. Before this they were only in the JSON. The harness now also records the mean and max of each metric next to the median.macOS RSS.
get_rssreadsri_resident_sizefromproc_pid_rusageinstead of forkingps, which the peak sampler was doing every 5ms. If that fails it tries/bin/psbefore thepsinPATH, because Nix's procpspscan't read RSS on macOS.Collect workers as they finish. The three scenario benchmarks use
Ractor.select, so a worker that finishes early isn't kept alive waiting behind a slower one.malloc_trim(0)before measuring retention. On glibc, retained RSS included memory Ruby had already freed. Where libc has nomalloc_trim(macOS, musl) the harness says so, andractor_mem_malloc_trimin the JSON records whether libc has it.RUBY_BENCH_PROFILING=1skips the GC settle, so a profile shows the scenario and not our retention GCs.Docs. More
--ractor-gcexamples, including the fact that Ractor benchmarks only run with--category ractor, and a section on profiling with samply.Because of the trim, Linux retained RSS from before this PR isn't comparable with numbers from after it.