Skip to content

Commit 0008f90

Browse files
committed
Extract parameters parser to its own object
1 parent 32f9e44 commit 0008f90

File tree

4 files changed

+674
-145
lines changed

4 files changed

+674
-145
lines changed

lib/argument_parser.rb

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
require 'optparse'
2+
require 'shellwords'
3+
4+
class ArgumentParser
5+
Args = Struct.new(
6+
:executables,
7+
:out_path,
8+
:out_override,
9+
:harness,
10+
:yjit_opts,
11+
:categories,
12+
:name_filters,
13+
:rss,
14+
:graph,
15+
:no_pinning,
16+
:turbo,
17+
:skip_yjit,
18+
:with_pre_init,
19+
keyword_init: true
20+
)
21+
22+
def self.parse(argv = ARGV)
23+
new.parse(argv)
24+
end
25+
26+
def parse(argv)
27+
args = default_args
28+
29+
OptionParser.new do |opts|
30+
opts.on("-e=NAME::RUBY_PATH OPTIONS", "ruby executable and options to be benchmarked (default: interp, yjit)") do |v|
31+
v.split(";").each do |name_executable|
32+
name, executable = name_executable.split("::", 2)
33+
if executable.nil?
34+
executable = name # allow skipping `NAME::`
35+
end
36+
args.executables[name] = executable.shellsplit
37+
end
38+
end
39+
40+
opts.on("--chruby=NAME::VERSION OPTIONS", "ruby version under chruby and options to be benchmarked") do |v|
41+
v.split(";").each do |name_version|
42+
name, version = name_version.split("::", 2)
43+
# Convert `ruby --yjit` to `ruby::ruby --yjit`
44+
if version.nil?
45+
version = name
46+
name = name.shellsplit.first
47+
end
48+
version, *options = version.shellsplit
49+
rubies_dir = ENV["RUBIES_DIR"] || "#{ENV["HOME"]}/.rubies"
50+
unless executable = ["/opt/rubies/#{version}/bin/ruby", "#{rubies_dir}/#{version}/bin/ruby"].find { |path| File.executable?(path) }
51+
abort "Cannot find '#{version}' in /opt/rubies or #{rubies_dir}"
52+
end
53+
args.executables[name] = [executable, *options]
54+
end
55+
end
56+
57+
opts.on("--out_path=OUT_PATH", "directory where to store output data files") do |v|
58+
args.out_path = v
59+
end
60+
61+
opts.on("--out-name=OUT_FILE", "write exactly this output file plus file extension, ignoring directories, overwriting if necessary") do |v|
62+
args.out_override = v
63+
end
64+
65+
opts.on("--category=headline,other,micro,ractor", "when given, only benchmarks with specified categories will run") do |v|
66+
args.categories += v.split(",")
67+
if args.categories == ["ractor"]
68+
args.harness = "harness-ractor"
69+
end
70+
end
71+
72+
opts.on("--headline", "when given, headline benchmarks will be run") do
73+
args.categories += ["headline"]
74+
end
75+
76+
opts.on("--name_filters=x,y,z", Array, "when given, only benchmarks with names that contain one of these strings will run") do |list|
77+
args.name_filters = list
78+
end
79+
80+
opts.on("--skip-yjit", "Don't run with yjit after interpreter") do
81+
args.skip_yjit = true
82+
end
83+
84+
opts.on("--harness=HARNESS_DIR", "which harness to use") do |v|
85+
v = "harness-#{v}" unless v.start_with?('harness')
86+
args.harness = v
87+
end
88+
89+
opts.on("--warmup=N", "the number of warmup iterations for the default harness (default: 15)") do |n|
90+
ENV["WARMUP_ITRS"] = n
91+
end
92+
93+
opts.on("--bench=N", "the number of benchmark iterations for the default harness (default: 10). Also defaults MIN_BENCH_TIME to 0.") do |n|
94+
ENV["MIN_BENCH_ITRS"] = n
95+
ENV["MIN_BENCH_TIME"] ||= "0"
96+
end
97+
98+
opts.on("--once", "benchmarks only 1 iteration with no warmup for the default harness") do
99+
ENV["WARMUP_ITRS"] = "0"
100+
ENV["MIN_BENCH_ITRS"] = "1"
101+
ENV["MIN_BENCH_TIME"] = "0"
102+
end
103+
104+
opts.on("--yjit-stats=STATS", "print YJIT stats at each iteration for the default harness") do |str|
105+
ENV["YJIT_BENCH_STATS"] = str
106+
end
107+
108+
opts.on("--zjit-stats=STATS", "print ZJIT stats at each iteration for the default harness") do |str|
109+
ENV["ZJIT_BENCH_STATS"] = str
110+
end
111+
112+
opts.on("--yjit_opts=OPT_STRING", "string of command-line options to run YJIT with (ignored if you use -e)") do |str|
113+
args.yjit_opts = str
114+
end
115+
116+
opts.on("--with_pre-init=PRE_INIT_FILE",
117+
"a file to require before each benchmark run, so settings can be tuned (eg. enable/disable GC compaction)") do |str|
118+
args.with_pre_init = str
119+
end
120+
121+
opts.on("--rss", "show RSS in the output (measured after benchmark iterations)") do
122+
args.rss = true
123+
end
124+
125+
opts.on("--graph", "generate a graph image of benchmark results") do
126+
args.graph = true
127+
end
128+
129+
opts.on("--no-pinning", "don't pin ruby to a specific CPU core") do
130+
args.no_pinning = true
131+
end
132+
133+
opts.on("--turbo", "don't disable CPU turbo boost") do
134+
args.turbo = true
135+
end
136+
end.parse!(argv)
137+
138+
# Remaining arguments are treated as benchmark name filters
139+
if argv.length > 0
140+
args.name_filters += argv
141+
end
142+
143+
args
144+
end
145+
146+
private
147+
148+
def default_args
149+
Args.new(
150+
executables: {},
151+
out_path: File.expand_path("./data"),
152+
out_override: nil,
153+
harness: "harness",
154+
yjit_opts: "",
155+
categories: [],
156+
name_filters: [],
157+
rss: false,
158+
graph: false,
159+
no_pinning: false,
160+
turbo: false,
161+
skip_yjit: false,
162+
with_pre_init: nil,
163+
)
164+
end
165+
end

run_benchmarks.rb

Lines changed: 3 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
#!/usr/bin/env ruby
22

3-
require 'optparse'
4-
require 'ostruct'
53
require 'pathname'
64
require 'fileutils'
7-
require 'shellwords'
85
require 'csv'
96
require 'json'
7+
require 'shellwords'
108
require 'rbconfig'
119
require 'etc'
1210
require 'yaml'
@@ -15,6 +13,7 @@
1513
require_relative 'lib/benchmark_runner'
1614
require_relative 'lib/table_formatter'
1715
require_relative 'lib/benchmark_filter'
16+
require_relative 'lib/argument_parser'
1817

1918
def have_yjit?(ruby)
2019
ruby_version = `#{ruby} -v --yjit 2> #{File::NULL}`.strip
@@ -158,135 +157,7 @@ def run_benchmarks(ruby:, ruby_description:, categories:, name_filters:, out_pat
158157
[bench_data, bench_failures]
159158
end
160159

161-
# Default values for command-line arguments
162-
args = OpenStruct.new({
163-
executables: {},
164-
out_path: File.expand_path("./data"),
165-
out_override: nil,
166-
harness: "harness",
167-
yjit_opts: "",
168-
categories: [],
169-
name_filters: [],
170-
rss: false,
171-
graph: false,
172-
no_pinning: false,
173-
turbo: false,
174-
skip_yjit: false,
175-
})
176-
177-
OptionParser.new do |opts|
178-
opts.on("-e=NAME::RUBY_PATH OPTIONS", "ruby executable and options to be benchmarked (default: interp, yjit)") do |v|
179-
v.split(";").each do |name_executable|
180-
name, executable = name_executable.split("::", 2)
181-
if executable.nil?
182-
executable = name # allow skipping `NAME::`
183-
end
184-
args.executables[name] = executable.shellsplit
185-
end
186-
end
187-
188-
opts.on("--chruby=NAME::VERSION OPTIONS", "ruby version under chruby and options to be benchmarked") do |v|
189-
v.split(";").each do |name_version|
190-
name, version = name_version.split("::", 2)
191-
# Convert `ruby --yjit` to `ruby::ruby --yjit`
192-
if version.nil?
193-
version = name
194-
name = name.shellsplit.first
195-
end
196-
version, *options = version.shellsplit
197-
rubies_dir = ENV["RUBIES_DIR"] || "#{ENV["HOME"]}/.rubies"
198-
unless executable = ["/opt/rubies/#{version}/bin/ruby", "#{rubies_dir}/#{version}/bin/ruby"].find { |path| File.executable?(path) }
199-
abort "Cannot find '#{version}' in /opt/rubies or #{rubies_dir}"
200-
end
201-
args.executables[name] = [executable, *options]
202-
end
203-
end
204-
205-
opts.on("--out_path=OUT_PATH", "directory where to store output data files") do |v|
206-
args.out_path = v
207-
end
208-
209-
opts.on("--out-name=OUT_FILE", "write exactly this output file plus file extension, ignoring directories, overwriting if necessary") do |v|
210-
args.out_override = v
211-
end
212-
213-
opts.on("--category=headline,other,micro,ractor", "when given, only benchmarks with specified categories will run") do |v|
214-
args.categories += v.split(",")
215-
if args.categories == ["ractor"]
216-
args.harness = "harness-ractor"
217-
end
218-
end
219-
220-
opts.on("--headline", "when given, headline benchmarks will be run") do
221-
args.categories += ["headline"]
222-
end
223-
224-
opts.on("--name_filters=x,y,z", Array, "when given, only benchmarks with names that contain one of these strings will run") do |list|
225-
args.name_filters = list
226-
end
227-
228-
opts.on("--skip-yjit", "Don't run with yjit after interpreter") do
229-
args.skip_yjit = true
230-
end
231-
232-
opts.on("--harness=HARNESS_DIR", "which harness to use") do |v|
233-
v = "harness-#{v}" unless v.start_with?('harness')
234-
args.harness = v
235-
end
236-
237-
opts.on("--warmup=N", "the number of warmup iterations for the default harness (default: 15)") do |n|
238-
ENV["WARMUP_ITRS"] = n
239-
end
240-
241-
opts.on("--bench=N", "the number of benchmark iterations for the default harness (default: 10). Also defaults MIN_BENCH_TIME to 0.") do |n|
242-
ENV["MIN_BENCH_ITRS"] = n
243-
ENV["MIN_BENCH_TIME"] ||= "0"
244-
end
245-
246-
opts.on("--once", "benchmarks only 1 iteration with no warmup for the default harness") do
247-
ENV["WARMUP_ITRS"] = "0"
248-
ENV["MIN_BENCH_ITRS"] = "1"
249-
ENV["MIN_BENCH_TIME"] = "0"
250-
end
251-
252-
opts.on("--yjit-stats=STATS", "print YJIT stats at each iteration for the default harness") do |str|
253-
ENV["YJIT_BENCH_STATS"] = str
254-
end
255-
256-
opts.on("--zjit-stats=STATS", "print ZJIT stats at each iteration for the default harness") do |str|
257-
ENV["ZJIT_BENCH_STATS"] = str
258-
end
259-
260-
opts.on("--yjit_opts=OPT_STRING", "string of command-line options to run YJIT with (ignored if you use -e)") do |str|
261-
args.yjit_opts=str
262-
end
263-
264-
opts.on("--with_pre-init=PRE_INIT_FILE",
265-
"a file to require before each benchmark run, so settings can be tuned (eg. enable/disable GC compaction)") do |str|
266-
args.with_pre_init = str
267-
end
268-
269-
opts.on("--rss", "show RSS in the output (measured after benchmark iterations)") do
270-
args.rss = true
271-
end
272-
273-
opts.on("--graph", "generate a graph image of benchmark results") do
274-
args.graph = true
275-
end
276-
277-
opts.on("--no-pinning", "don't pin ruby to a specific CPU core") do
278-
args.no_pinning = true
279-
end
280-
281-
opts.on("--turbo", "don't disable CPU turbo boost") do
282-
args.turbo = true
283-
end
284-
end.parse!
285-
286-
# Remaining arguments are treated as benchmark name filters
287-
if ARGV.length > 0
288-
args.name_filters += ARGV
289-
end
160+
args = ArgumentParser.parse(ARGV)
290161

291162
# If -e is not specified, benchmark the current Ruby. Compare it with YJIT if available.
292163
if args.executables.empty?

0 commit comments

Comments
 (0)