@@ -12,6 +12,7 @@ def initialize(executable_names:, bench_data:, include_rss: false, include_pvalu
1212 @include_pvalue = include_pvalue
1313 @zjit_stats = zjit_stats || [ ]
1414 @include_gc = detect_gc_data ( bench_data )
15+ @rss_has_samples = @include_rss && detect_rss_samples ( bench_data )
1516 @base_name = executable_names . first
1617 @other_names = executable_names [ 1 ..]
1718 @bench_names = compute_bench_names
@@ -86,7 +87,7 @@ def build_format
8687
8788 @executable_names . each do |_name |
8889 format << "%s"
89- format << "% .1f" if @include_rss
90+ format << ( @rss_has_samples ? "%s" : "% .1f") if @include_rss
9091 @zjit_stats . each { format << "%s" }
9192 if @include_gc
9293 format << "%s"
@@ -125,11 +126,15 @@ def build_row(bench_name)
125126 t0s = extract_first_iteration_times ( bench_name )
126127 times_no_warmup = extract_benchmark_times ( bench_name )
127128 rsss = extract_rss_values ( bench_name )
129+ rss_series = @rss_has_samples ? extract_rss_series ( bench_name ) : nil
128130
129131 base_t0 , *other_t0s = t0s
130132 base_t , *other_ts = times_no_warmup
131133 base_rss , *other_rsss = rsss
132134
135+ base_rss_cell = rss_cell ( base_rss , rss_series && rss_series [ 0 ] )
136+ other_rss_cells = other_rsss . each_index . map { |i | rss_cell ( other_rsss [ i ] , rss_series && rss_series [ i + 1 ] ) }
137+
133138 # Extract zjit stats: { stat_name => [base_val, other1_val, ...] }
134139 zjit_stat_values = @zjit_stats . map do |stat |
135140 [ stat , extract_zjit_stat ( bench_name , stat ) ]
@@ -143,8 +148,8 @@ def build_row(bench_name)
143148 end
144149
145150 row = [ bench_name ]
146- build_base_columns ( row , base_t , base_rss , zjit_stat_values , 0 , base_mark , base_sweep )
147- build_comparison_columns ( row , other_ts , other_rsss , zjit_stat_values , other_marks , other_sweeps )
151+ build_base_columns ( row , base_t , base_rss_cell , zjit_stat_values , 0 , base_mark , base_sweep )
152+ build_comparison_columns ( row , other_ts , other_rss_cells , zjit_stat_values , other_marks , other_sweeps )
148153 build_ratio_columns ( row , base_t0 , other_t0s , base_t , other_ts )
149154 build_rss_ratio_columns ( row , base_rss , other_rsss )
150155 build_gc_ratio_columns ( row , base_mark , other_marks , base_sweep , other_sweeps )
@@ -162,10 +167,10 @@ def build_base_columns(row, base_t, base_rss, zjit_stat_values, exe_index, base_
162167 end
163168 end
164169
165- def build_comparison_columns ( row , other_ts , other_rsss , zjit_stat_values , other_marks , other_sweeps )
170+ def build_comparison_columns ( row , other_ts , other_rss_cells , zjit_stat_values , other_marks , other_sweeps )
166171 other_ts . each_with_index do |other_t , i |
167172 row << format_time_with_stddev ( other_t )
168- row << other_rsss [ i ] if @include_rss
173+ row << other_rss_cells [ i ] if @include_rss
169174 zjit_stat_values . each { |_stat , values | row << format_stat ( values [ i + 1 ] ) }
170175 if @include_gc
171176 row << format_time_with_stddev ( other_marks [ i ] )
@@ -283,9 +288,38 @@ def extract_benchmark_times(bench_name)
283288 end
284289 end
285290
291+ # Numeric RSS (MiB) per executable, used for the RSS ratio. When per-iteration
292+ # samples are present we use their mean so the ratio matches the displayed value.
286293 def extract_rss_values ( bench_name )
287294 @executable_names . map do |name |
288- bench_data_for ( name , bench_name ) [ 'rss' ] / BYTES_TO_MIB
295+ data = bench_data_for ( name , bench_name )
296+ samples = data [ 'rss_samples' ]
297+ if samples . is_a? ( Array ) && !samples . empty?
298+ mean ( samples ) / BYTES_TO_MIB
299+ else
300+ data [ 'rss' ] / BYTES_TO_MIB
301+ end
302+ end
303+ end
304+
305+ # Per-iteration RSS samples (MiB) per executable, or nil when a run lacks them.
306+ def extract_rss_series ( bench_name )
307+ @executable_names . map do |name |
308+ samples = bench_data_for ( name , bench_name ) [ 'rss_samples' ]
309+ next nil unless samples . is_a? ( Array ) && !samples . empty?
310+ samples . map { |bytes | bytes / BYTES_TO_MIB }
311+ end
312+ end
313+
314+ # Display value for an RSS column: mean ± stddev% when samples exist (matching
315+ # the timing columns), otherwise a plain MiB value. Returns a Float when no run
316+ # in the suite has samples, preserving the legacy "%.1f" formatting.
317+ def rss_cell ( mean_value , series )
318+ return mean_value unless @rss_has_samples
319+ if series && !series . empty?
320+ format_time_with_stddev ( series )
321+ else
322+ "%.1f" % mean_value
289323 end
290324 end
291325
@@ -305,6 +339,12 @@ def detect_gc_data(bench_data)
305339 bench_data . values . any? { |benchmarks | benchmarks . values . any? { |d | d . is_a? ( Hash ) && d . key? ( 'gc_marking_time_bench' ) } }
306340 end
307341
342+ def detect_rss_samples ( bench_data )
343+ bench_data . values . any? do |benchmarks |
344+ benchmarks . values . any? { |d | d . is_a? ( Hash ) && d [ 'rss_samples' ] . is_a? ( Array ) && !d [ 'rss_samples' ] . empty? }
345+ end
346+ end
347+
308348 def bench_data_for ( name , bench_name )
309349 @bench_data [ name ] [ bench_name ]
310350 end
0 commit comments