Skip to content

Commit b4a090e

Browse files
committed
Improve the run_once script to better handle Ruby options and harness selection
So we don't need to deal with the `-r` ruby argument directly for selecting harnesses.
1 parent 4c84b90 commit b4a090e

File tree

9 files changed

+372
-46
lines changed

9 files changed

+372
-46
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ jobs:
141141
with:
142142
ruby-version: head
143143

144-
- name: Test run_once.sh
145-
run: ./run_once.sh --yjit-stats benchmarks/railsbench/benchmark.rb
144+
- name: Test run_once.rb
145+
run: ./run_once.rb -- --yjit-stats benchmarks/railsbench/benchmark.rb
146146

147147
all-tests:
148148
runs-on: ubuntu-latest

README.md

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ To run one or more specific benchmarks and record the data:
9393

9494
### Running a single benchmark
9595

96-
This is the easiest way to run a single benchmark.
97-
It requires no setup at all and assumes nothing about the Ruby you are benchmarking.
98-
It's also convenient for profiling, debugging, etc, especially since all benchmarked code runs in that process.
96+
The easiest way to run a single benchmark once is using the `run_once.rb` script:
9997

10098
```
101-
ruby benchmarks/some_benchmark.rb
99+
./run_once.rb benchmarks/some_benchmark.rb
102100
```
103101

102+
This automatically sets the environment to run the benchmark once (no warmup iterations).
103+
104104
### Benchmark organization
105105

106106
Benchmarks can be organized in three ways:
@@ -225,23 +225,29 @@ You can find several test harnesses in the `harness/` directory:
225225
* `chain` - a harness to chain multiple harnesses together
226226
* `mplr` - a harness for multiple iterations with time limits
227227

228-
To use a specific harness, run a benchmark script directly with `-r` to require the harness:
228+
To use a specific harness, use the `run_once.rb` script:
229229

230230
```
231-
# Use default harness (loads harness/default.rb automatically)
232-
ruby benchmarks/railsbench/benchmark.rb
231+
# Use default harness
232+
./run_once.rb benchmarks/railsbench/benchmark.rb
233233
234234
# Use the 'once' harness
235-
ruby -r./harness/once benchmarks/railsbench/benchmark.rb
235+
./run_once.rb --harness=once benchmarks/railsbench/benchmark.rb
236236
237237
# Use the 'perf' harness
238-
ruby -r./harness/perf benchmarks/railsbench/benchmark.rb
238+
./run_once.rb --harness=perf benchmarks/railsbench/benchmark.rb
239239
240240
# Use the 'stackprof' harness
241-
ruby -r./harness/stackprof benchmarks/railsbench/benchmark.rb
241+
./run_once.rb --harness=stackprof benchmarks/railsbench/benchmark.rb
242242
243243
# Use the 'vernier' harness
244-
ruby -r./harness/vernier benchmarks/railsbench/benchmark.rb
244+
./run_once.rb --harness=vernier benchmarks/railsbench/benchmark.rb
245+
246+
# Pass Ruby options like --yjit (use -- separator)
247+
./run_once.rb -- --yjit benchmarks/railsbench/benchmark.rb
248+
249+
# Combine harness option with Ruby options
250+
./run_once.rb --harness=default -- --yjit-stats benchmarks/railsbench/benchmark.rb
245251
```
246252

247253
When using `run_benchmarks.rb`, you can specify a harness with the `--harness` option:
@@ -272,12 +278,11 @@ You can also use `--warmup`, `--bench`, or `--once` to set these environment var
272278
./run_benchmarks.rb railsbench --once
273279
```
274280

275-
There is also a handy script for running benchmarks just once using
276-
`WARMUP_ITRS=0 MIN_BENCH_ITRS=1 MIN_BENCH_TIME=0`, for example
281+
You can also use the `run_once.rb` script to run benchmarks just once, for example
277282
with the `--yjit-stats` command-line option:
278283

279284
```
280-
./run_once.sh --yjit-stats benchmarks/railsbench/benchmark.rb
285+
./run_once.rb -- --yjit-stats benchmarks/railsbench/benchmark.rb
281286
```
282287

283288
### Using perf
@@ -287,10 +292,10 @@ If `PERF` environment variable is present, it starts the perf subcommand after w
287292

288293
```sh
289294
# Use `perf record` for both warmup and benchmark
290-
perf record ruby --yjit-perf=map -r./harness/perf benchmarks/railsbench/benchmark.rb
295+
perf record ./run_once.rb --harness=perf -- --yjit-perf=map benchmarks/railsbench/benchmark.rb
291296

292297
# Use `perf record` only for benchmark
293-
PERF=record ruby --yjit-perf=map -r./harness/perf benchmarks/railsbench/benchmark.rb
298+
PERF=record ./run_once.rb --harness=perf -- --yjit-perf=map benchmarks/railsbench/benchmark.rb
294299
```
295300

296301
This is the only harness that uses `run_benchmark`'s argument, `num_itrs_hint`.

harness/perf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# This is a relatively minimal harness meant for use with Linux perf(1).
44
# Example usage:
55
#
6-
# $ PERF='record -e cycles' ruby -r./harness/perf benchmarks/fib.rb
6+
# $ PERF='record -e cycles' ./run_once.rb --harness=perf benchmarks/fib.rb
77
#
88
# When recording with perf(1), make sure the benchmark runs long enough; you
99
# can tweak the MIN_BENCH_ITRS environment variable to lengthen the run. A race

harness/stackprof.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# Profile the benchmark (ignoring initialization code) with stackprof.
44
# Customize stackprof options with an env var of STACKPROF_OPTS='key:value,...'.
55
# Usage:
6-
# STACKPROF_OPTS='mode:object' MIN_BENCH_TIME=0 MIN_BENCH_ITRS=1 ruby -v -r./harness/stackprof benchmarks/.../benchmark.rb
7-
# STACKPROF_OPTS='mode:cpu,interval:10' MIN_BENCH_TIME=1 MIN_BENCH_ITRS=10 ruby -v -r./harness/stackprof benchmarks/.../benchmark.rb
6+
# STACKPROF_OPTS='mode:object' ./run_once.rb --harness=stackprof benchmarks/.../benchmark.rb
7+
# STACKPROF_OPTS='mode:cpu,interval:10' MIN_BENCH_ITRS=10 ./run_once.rb --harness=stackprof benchmarks/.../benchmark.rb
88

99
require_relative "../lib/harness"
1010
require_relative "../lib/harness/extra"

harness/vernier.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# Profile the benchmark (ignoring initialization code) using vernier and display the profile.
44
# Set NO_VIERWER=1 to disable automatically opening the profile in a browser.
55
# Usage:
6-
# MIN_BENCH_TIME=1 MIN_BENCH_ITRS=1 ruby -v -r./harness/vernier benchmarks/...
7-
# NO_VIEWER=1 MIN_BENCH_TIME=1 MIN_BENCH_ITRS=1 ruby -v -r./harness/vernier benchmarks/...
6+
# ./run_once.rb --harness=vernier benchmarks/...
7+
# NO_VIEWER=1 ./run_once.rb --harness=vernier benchmarks/...
88

99
require_relative "../lib/harness"
1010
require_relative "../lib/harness/extra"

lib/harness/loader.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# Use harness/default.rb by default. You can change it with -r option.
2-
# The -r option should be specified BEFORE requiring loader.
1+
# Use harness/default.rb by default. You can change it with the --harness option in run_once.rb
2+
# or with -r option when calling Ruby directly.
33
# Examples:
4-
# ruby benchmarks/railsbench/benchmark.rb # uses harness/default.rb
5-
# ruby -r./harness/once benchmarks/railsbench/benchmark.rb # uses harness/once.rb
6-
# ruby -r./harness/ractor benchmarks/railsbench/benchmark.rb # uses harness/ractor.rb
4+
# ./run_once.rb benchmarks/railsbench/benchmark.rb # uses harness/default.rb
5+
# ./run_once.rb --harness=once benchmarks/railsbench/benchmark.rb # uses harness/once.rb
6+
# ./run_once.rb --harness=ractor benchmarks/railsbench/benchmark.rb # uses harness/ractor.rb
77

88
# Only load the default harness if no other harness has defined run_benchmark
99
unless defined?(run_benchmark)

run_once.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
# Script to run a single benchmark once
5+
# Provides a clean interface for running benchmarks with different harnesses.
6+
# Examples:
7+
# ./run_once.rb benchmarks/railsbench/benchmark.rb
8+
# ./run_once.rb --harness=once benchmarks/fib.rb
9+
# ./run_once.rb --harness=stackprof benchmarks/fib.rb
10+
# ./run_once.rb -- --yjit-stats benchmarks/railsbench/benchmark.rb
11+
# ./run_once.rb --harness=default -- --yjit benchmarks/fib.rb
12+
13+
require 'optparse'
14+
require 'shellwords'
15+
16+
# Parse options
17+
harness = nil
18+
ruby_args = []
19+
benchmark_file = nil
20+
21+
parser = OptionParser.new do |opts|
22+
opts.banner = "Usage: #{$0} [options] [--] [ruby-options] BENCHMARK_FILE"
23+
24+
opts.on("--harness=HARNESS", "Harness to use (default: default, options: once, bips, perf, ractor, stackprof, vernier, warmup, stats, continuous, chain, mplr)") do |h|
25+
harness = h
26+
end
27+
28+
opts.on("-h", "--help", "Show this help message") do
29+
puts opts
30+
exit
31+
end
32+
end
33+
34+
# Parse our options - this will stop at '--' or first non-option argument
35+
begin
36+
parser.parse!
37+
rescue OptionParser::InvalidOption => e
38+
puts "Error: #{e.message}"
39+
puts parser
40+
exit 1
41+
end
42+
43+
# After parsing, ARGV contains remaining args (Ruby options + benchmark file)
44+
ARGV.each do |arg|
45+
if arg.end_with?('.rb') && !benchmark_file
46+
benchmark_file = arg
47+
else
48+
ruby_args << arg
49+
end
50+
end
51+
52+
if !benchmark_file
53+
puts "Error: No benchmark file specified"
54+
puts parser
55+
exit 1
56+
end
57+
58+
unless File.exist?(benchmark_file)
59+
puts "Error: Benchmark file not found: #{benchmark_file}"
60+
exit 1
61+
end
62+
63+
# Automatically detect ractor benchmarks
64+
if !harness && benchmark_file.include?('benchmarks-ractor/')
65+
harness = 'ractor'
66+
end
67+
68+
# Build the command
69+
harness_dir = File.expand_path('harness', __dir__)
70+
harness_args = if harness && harness != 'default'
71+
harness_path = File.join(harness_dir, harness)
72+
unless File.exist?("#{harness_path}.rb")
73+
puts "Error: Harness not found: #{harness}"
74+
puts "Available harnesses: #{Dir.glob("#{harness_dir}/*.rb").map { |f| File.basename(f, '.rb') }.join(', ')}"
75+
exit 1
76+
end
77+
['-r', harness_path]
78+
else
79+
[]
80+
end
81+
82+
# Set environment for running once
83+
ENV['WARMUP_ITRS'] = '0'
84+
ENV['MIN_BENCH_ITRS'] = '1'
85+
ENV['MIN_BENCH_TIME'] = '0'
86+
87+
# Build and execute the command
88+
cmd = ['ruby', *ruby_args, *harness_args, benchmark_file]
89+
90+
exec(*cmd)

run_once.sh

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)