Benchmarker.rb
$Release: 1.0.0 $
GitHub: https://github.com/kwatch/benchmarker/tree/main/ruby
Overview
Benchmarker.rb is an awesome benchmarking tool for Ruby.
- Easy to use
- Pretty good output (including JSON format)
- Rich features compared to
benchmark.rb(standard library)- Iterate benchmarks and calculate average of resutls (optional)
- Remove min and max results to exclude abnormal values (optional)
- Remove loop times from each benchmark results (optional)
- Save benchmark results into JSON file (optional)
- Change loop times, number of iteration, etc by command-line option
- Print platform information automatically
- Print ranking graph and ratio matrix automatically
Table of contents
Install
https://rubygems.org/gems/benchmarker
$ gem install benchmarker
Quick example
Create sample script:
$ ruby -r benchmarker -e '' -- -S > bench.rb
File: bench.rb
# -*- coding: utf-8 -*-
require 'benchmarker' # https://kwatch.github.io/benchmarker/ruby.html
nums = (1..10000).to_a
title = "calculate sum of integers"
Benchmarker.scope(title, width: 24, loop: 1000, iter: 5, extra: 1) do
## other options -- inverse: true, outfile: "result.json", quiet: true,
## sleep: 1, colorize: true, filter: "task=*foo*"
## hooks
#before_all do end
#after_all do end
#before do end # or: before do |task_name, tag| end
#after do end # or: after do |task_name, tag| end
## tasks
task nil do # empty-loop task
# do nothing
end
task "each() & '+='" do
total = 0
nums.each {|n| total += n }
total
end
task "inject()" do
total = nums.inject(0) {|t, n| t += n }
total
end
task "while statement" do
total = 0; i = -1; len = nums.length
while (i += 1) < len
total += nums[i]
end
total
end
#task "name", tag: "curr", skip: (!condition ? nil : "...reason...") do
# ... run benchmark code ...
#end
## validation
validate do |val| # or: validate do |val, task_name, tag|
n = nums.last
expected = n * (n+1) / 2
assert_eq val, expected
# or: assert val == expected, "expected #{expected} but got #{val}"
end
end
Output example:
$ ruby bench.rb --help | less $ ruby bench.rb -q # or: ruby bench.rb -w 24 -n 1000 -i 5 -x 1 -q ## title: sum of integers ## options: loop=1000, iter=5, extra=1 ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## Removed Min & Max min iter max iter each() & '+=' 0.4715 (#6) 0.5257 (#1) inject() 0.6087 (#3) 0.6262 (#6) while statement 0.3271 (#5) 0.3352 (#1) ## Average of 5 (=7-2*1) user sys total real each() & '+=' 0.4760 0.0020 0.4780 0.4789 inject() 0.6080 0.0000 0.6080 0.6122 while statement 0.3280 0.0000 0.3280 0.3303 ## Ranking real while statement 0.3303 (100.0%) ******************** each() & '+=' 0.4789 ( 69.0%) ************** inject() 0.6122 ( 54.0%) *********** ## Matrix real [1] [2] [3] [1] while statement 0.3303 100.0% 145.0% 185.3% [2] each() & '+=' 0.4789 69.0% 100.0% 127.8% [3] inject() 0.6122 54.0% 78.2% 100.0%
Step by step tutorial
Basic usage
File: ex1.rb
require 'benchmarker'
title = "string concat" # optional
Benchmarker.scope(title, width: 22) do
loop = 1000 * 1000
s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon"
task "String#+" do
loop.times do
sos = s1 + s2 + s3 + s4 + s5
end
end
task "String#<<" do
loop.times do
(sos = "") << s1 << s2 << s3 << s4 << s5
end
end
task "Array#join" do
loop.times do
sos = [s1, s2, s3, s4, s5].join()
end
end
task "Interpolation" do
loop.times do
sos = "#{s1}#{s2}#{s3}#{s4}#{s5}"
end
end
end
You can omit title argument, for example Benchamrker.scope(width: 22).
Output example:
$ ruby ex1.rb ## title: string concat ## options: loop=1, iter=1, extra=0 ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## user sys total real String#+ 0.5500 0.0000 0.5500 0.5557 String#<< 0.5700 0.0000 0.5700 0.5839 Array#join 0.9100 0.0100 0.9200 0.9235 Interpolation 0.4900 0.0000 0.4900 0.4938 ## Ranking real Interpolation 0.4938 (100.0%) ******************** String#+ 0.5557 ( 88.9%) ****************** String#<< 0.5839 ( 84.6%) ***************** Array#join 0.9235 ( 53.5%) *********** ## Matrix real [1] [2] [3] [4] [1] Interpolation 0.4938 100.0% 112.5% 118.2% 187.0% [2] String#+ 0.5557 88.9% 100.0% 105.1% 166.2% [3] String#<< 0.5839 84.6% 95.2% 100.0% 158.1% [4] Array#join 0.9235 53.5% 60.2% 63.2% 100.0%
Number of loop
You can specify number of loop in script and/or command-line option.
File: ex2.rb
require 'benchmarker' title = "string concat" Benchmarker.scope(title, width: 22, loop: 1000*1000) doloop = 1000 * 1000s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon" task "String#+" doloop.times dosos = s1 + s2 + s3 + s4 + s5endend task "String#<<" doloop.times do(sos = "") << s1 << s2 << s3 << s4 << s5endend task "Array#join" doloop.times dosos = [s1, s2, s3, s4, s5].join()endend task "Interpolation" doloop.times dosos = "#{s1}#{s2}#{s3}#{s4}#{s5}"endend end
Output example:
$ ruby ex2.rb # or: ruby ex2.rb -n 1000000 ## title: string concat ## options: loop=1000000, iter=1, extra=0 ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## user sys total real String#+ 0.5700 0.0000 0.5700 0.5760 String#<< 0.6100 0.0000 0.6100 0.6153 Array#join 0.9200 0.0200 0.9400 0.9401 Interpolation 0.5200 0.0000 0.5200 0.5276 ## Ranking real Interpolation 0.5276 (100.0%) ******************** String#+ 0.5760 ( 91.6%) ****************** String#<< 0.6153 ( 85.7%) ***************** Array#join 0.9401 ( 56.1%) *********** ## Matrix real [1] [2] [3] [4] [1] Interpolation 0.5276 100.0% 109.2% 116.6% 178.2% [2] String#+ 0.5760 91.6% 100.0% 106.8% 163.2% [3] String#<< 0.6153 85.7% 93.6% 100.0% 152.8% [4] Array#join 0.9401 56.1% 61.3% 65.4% 100.0%
Notice that command-line option -n <N> overwrites Benchmarker.scope(loop: <N>).
Empty loop task
Empty loop task is used to subtract overhead time of loop from entire time.
File: ex3.rb
require 'benchmarker'
title = "string concat"
Benchmarker.scope(title, width: 22, loop: 1000*1000) do
s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon"
task nil do
nil
end
task "String#+" do
sos = s1 + s2 + s3 + s4 + s5
end
task "String#<<" do
(sos = "") << s1 << s2 << s3 << s4 << s5
end
task "Array#join" do
sos = [s1, s2, s3, s4, s5].join()
end
task "Interpolation" do
sos = "#{s1}#{s2}#{s3}#{s4}#{s5}"
end
end
Output example:
$ ruby ex3.rb ## title: string concat ## options: loop=1000000, iter=1, extra=0 ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## user sys total real (Empty) 0.0900 0.0000 0.0900 0.0896 String#+ 0.5000 0.0000 0.5000 0.4957 String#<< 0.5300 0.0000 0.5300 0.5373 Array#join 0.8400 0.0100 0.8500 0.8525 Interpolation 0.4500 0.0000 0.4500 0.4559 ## Ranking real Interpolation 0.4559 (100.0%) ******************** String#+ 0.4957 ( 92.0%) ****************** String#<< 0.5373 ( 84.9%) ***************** Array#join 0.8525 ( 53.5%) *********** ## Matrix real [1] [2] [3] [4] [1] Interpolation 0.4559 100.0% 108.7% 117.9% 187.0% [2] String#+ 0.4957 92.0% 100.0% 108.4% 172.0% [3] String#<< 0.5373 84.9% 92.3% 100.0% 158.7% [4] Array#join 0.8525 53.5% 58.1% 63.0% 100.0%
For example, actual time of 'String#+' entry is 0.5853 sec (= 0.4957 + 0.0896). In other words, real time (0.4957 sec) is already subtracted empty loop time (0.0896 sec).
Actual time of each benchmark task:
- - String#+
- 0.5853 sec (= 0.4957 + 0.0896)
- - String#<<
- 0.6269 sec (= 0.5373 + 0.0896)
- - Array#join
- 0.9421 sec (= 0.8525 + 0.0896)
- - Interpolation
- 0.5455 sec (= 0.4559 + 0.0896)
Iteration
It is possible to iterate all benchmark tasks. Average of results are calculated automatically.
Benchmarker.scope(iter: 5)or command-line option-i 5iterates all benchmark tasks 5 times and reports average of result.Benchmarker.scope(extra: 1)or command-line option-x 1increases number of iterations by2*1times, and excludes min and max results before calculating average.Benchmarker.scope(iter: 5, extra: 1)or command-line option-i 5 -x 1iterates benchmarks 7 times (= 5+2*1) , excludes min and max results, and calculates average of 5 results.
File: ex4.rb
require 'benchmarker'
title = "string concat"
Benchmarker.scope(title, width: 22, loop: 1000*1000, iter: 5, extra: 1) do
s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon"
task nil do
nil
end
task "String#+" do
sos = s1 + s2 + s3 + s4 + s5
end
task "String#<<" do
(sos = "") << s1 << s2 << s3 << s4 << s5
end
task "Array#join" do
sos = [s1, s2, s3, s4, s5].join()
end
task "Interpolation" do
sos = "#{s1}#{s2}#{s3}#{s4}#{s5}"
end
end
Output example:
$ ruby ex4.rb # or: ruby ext4.rb -i 5 -x 1 ## title: string concat ## options: loop=1000000, iter=5, extra=1 ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## (#1) user sys total real (Empty) 0.0900 0.0000 0.0900 0.0889 String#+ 0.4900 0.0000 0.4900 0.4906 String#<< 0.5100 0.0100 0.5200 0.5216 Array#join 0.8200 0.0100 0.8300 0.8346 Interpolation 0.4400 0.0000 0.4400 0.4404 ## (#2) user sys total real (Empty) 0.0800 0.0000 0.0800 0.0824 String#+ 0.4600 0.0000 0.4600 0.4642 String#<< 0.5200 0.0100 0.5300 0.5202 Array#join 0.8100 0.0100 0.8200 0.8175 Interpolation 0.4400 0.0000 0.4400 0.4461 ## (#3) user sys total real (Empty) 0.0900 0.0000 0.0900 0.0824 String#+ 0.4500 0.0000 0.4500 0.4661 String#<< 0.5100 0.0000 0.5100 0.5149 Array#join 0.8100 0.0100 0.8200 0.8340 Interpolation 0.4300 0.0100 0.4400 0.4454 ## (#4) user sys total real (Empty) 0.0900 0.0000 0.0900 0.0826 String#+ 0.4600 0.0000 0.4600 0.4738 String#<< 0.5100 0.0000 0.5100 0.5219 Array#join 0.8200 0.0100 0.8300 0.8475 Interpolation 0.4400 0.0000 0.4400 0.4459 ## (#5) user sys total real (Empty) 0.0800 0.0000 0.0800 0.0824 String#+ 0.4700 0.0000 0.4700 0.4652 String#<< 0.5200 0.0100 0.5300 0.5275 Array#join 0.8400 0.0100 0.8500 0.8527 Interpolation 0.4800 0.0000 0.4800 0.4815 ## (#6) user sys total real (Empty) 0.0900 0.0000 0.0900 0.0865 String#+ 0.4800 0.0000 0.4800 0.4889 String#<< 0.5400 0.0100 0.5500 0.5526 Array#join 0.8400 0.0100 0.8500 0.8532 Interpolation 0.4600 0.0000 0.4600 0.4653 ## (#7) user sys total real (Empty) 0.0800 0.0000 0.0800 0.0882 String#+ 0.5000 0.0000 0.5000 0.4909 String#<< 0.5500 0.0000 0.5500 0.5489 Array#join 0.8500 0.0100 0.8600 0.8491 Interpolation 0.4700 0.0100 0.4800 0.4649 ## Removed Min & Max min iter max iter String#+ 0.4642 (#2) 0.4909 (#7) String#<< 0.5149 (#3) 0.5526 (#6) Array#join 0.8175 (#2) 0.8532 (#6) Interpolation 0.4404 (#1) 0.4815 (#5) ## Average of 5 (=7-2*1) user sys total real String#+ 0.4700 0.0000 0.4700 0.4769 String#<< 0.5220 0.0060 0.5280 0.5280 Array#join 0.8280 0.0100 0.8380 0.8436 Interpolation 0.4480 0.0040 0.4520 0.4535 ## Ranking real Interpolation 0.4535 (100.0%) ******************** String#+ 0.4769 ( 95.1%) ******************* String#<< 0.5280 ( 85.9%) ***************** Array#join 0.8436 ( 53.8%) *********** ## Matrix real [1] [2] [3] [4] [1] Interpolation 0.4535 100.0% 105.2% 116.4% 186.0% [2] String#+ 0.4769 95.1% 100.0% 110.7% 176.9% [3] String#<< 0.5280 85.9% 90.3% 100.0% 159.8% [4] Array#join 0.8436 53.8% 56.5% 62.6% 100.0%
If you want to print only total result (= ignore results of each iteration),
add command-line option -q, or add quiet: true to Benchmarker.scope().
$ ruby ex4.rb -q # ignore results of each iteration ## title: string concat ## options: loop=1000000, iter=5, extra=1 ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## Removed Min & Max min iter max iter String#+ 0.4642 (#2) 0.4909 (#7) String#<< 0.5149 (#3) 0.5526 (#6) Array#join 0.8175 (#2) 0.8532 (#6) Interpolation 0.4404 (#1) 0.4815 (#5) ## Average of 5 (=7-2*1) user sys total real String#+ 0.4700 0.0000 0.4700 0.4769 String#<< 0.5220 0.0060 0.5280 0.5280 Array#join 0.8280 0.0100 0.8380 0.8436 Interpolation 0.4480 0.0040 0.4520 0.4535 ## Ranking real Interpolation 0.4535 (100.0%) ******************** String#+ 0.4769 ( 95.1%) ******************* String#<< 0.5280 ( 85.9%) ***************** Array#join 0.8436 ( 53.8%) *********** ## Matrix real [1] [2] [3] [4] [1] Interpolation 0.4535 100.0% 105.2% 116.4% 186.0% [2] String#+ 0.4769 95.1% 100.0% 110.7% 176.9% [3] String#<< 0.5280 85.9% 90.3% 100.0% 159.8% [4] Array#join 0.8436 53.8% 56.5% 62.6% 100.0%
Tags
task() can take user-defined tags. They can be string or array of strings.
Example:
Benchmarker.scope(loop: 1000*1000) do
task "Interpolation", tag: 'curr' do # or: tag: ['curr']
....
end
## or
task "Interpolation", <<-'END', binding(), tag: 'curr'
....
END
...
end
Tags are useful to filter or categorize tasks. See the following sections for details.
Filters
Using command-line option -F, you can filter benchmarks by name or tag.
Example:
## filter by task name $ ruby ex4.rb -F task='*String*' # select tasks matched to pattern $ ruby ex4.rb -F task!='*String*' # reject tasks matched to pattern ## filter by tag $ ruby ex4.rb -F tag='curr' # select only tasks tagged as 'curr' $ ruby ex4.rb -F tag!='curr' # reject all tasks tagged as 'curr'
Meta characters *, ?, [], and [] are avaible in filter string.
Benchmarker.scope(filter: ...) is equivarent to -F ... option.
For example, if you want to skip heavy benchmark tasks by default:
## skip benchmarks tagged as 'heavy'
Benchmarker.scope(title, filter: "tag!=heavy") do |bm|
task "Too heavy benchmark", tag: 'heavy' do
# do heavy benchamark
end
....
Command-line example:
$ ruby ex4.rb # skip heavy benchmark tasks $ ruby ex4.rb -F 'tag!=x' # run all benchmark tasks
Hooks
Benchmarker provides several hook methods.
before do ... end: do something before each task.after do ... end: do something after each task.before_all do ... end: do something once before all tasks.after_all do ... end: do something once after all tasks.
For example:
require 'benchmarker'
Benchmarker.scope() do
before do |task_name|
...
end
after do |task_name|
...
end
before_all do
...
end
after_all do
...
end
task "AAA" do
...
end
task "BBB" do
...
end
end
In above example, following blocks are called in this order.
- hook
before_all - hook
before - task
"AAA" - hook
after - hook
before - task
"BBB" - hook
after - hook
after_all
before and after hooks can accept task name and tag value.
You can switch hook operation according to task name or tag value.
before do |task_name, tag|
....
end
after do |task_name, tag|
....
end
Validation
It is very important to write benchmark task program correctly. You should validate result of benchmark task.
Benchmarker supports to validate result value of benchmark tasks.
File: ex5.rb
require 'benchmarker'
title = "string concat"
Benchmarker.scope(title, width: 22, loop: 1000*1000, iter: 5, extra: 1) do
s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon"
task nil do
nil
end
task "String#+" do
sos = s1 + s2 + s3 + s4 + s5
sos
end
task "String#<<" do
(sos = "") << s1 << s2 << s3 << s4 << s5
sos
end
task "Array#join" do
sos = [s1, s2, s3, s4, s5].join()
sos
end
task "Interpolation" do
sos = "#{s1}#{s2}#{s3}#{s4}#{s5}"
sos
end
validate do |sos|
expected = "HaruhiMikuruYukiItsukiKyon"
assert_eq sos, expected
## or
assert sos == expected,
"expected #{expected.inspect} but got #{sos.inspect}"
end
end
- Validation is invoked after each task invocation, so it doesn't affect to benchmark result.
- If validation failed, error will be raised and benchmark script will be stopped.
- Validator can accept task name and tag value. You can switch validation code according to benchmark task name or tag value.
....
validate do |sos, task_name, tag|
expected = "HaruhiMikuruYukiItsukiKyon"
if task_name == "Interporation"
assert_eq sos, expected
end
end
....
More accurate benchmarks
To measure benchmark accurately, it is important to remove (or reduce) overhead of loop. As described before, Benchmaker provides empty-loop task feature for this purpose.
Benchmarker provides another way to reduce overhead of loop: If you specify benchmark task code by string instead of block argument, Benchmarker repeats the code string 100 times and generates block argument from it.
## this code...
task "foo", "x = 1+2+3"
## ...is converted into:
task "foo" do
x = 1+2+3
x = 1+2+3
x = 1+2+3
x = 1+2+3
.... # (repeat 100 times)
end
As a result, overhead of loop is reduced into 1/100 and you can measure benchmarks more accurately.
To generate block argument from code string, Benchmarker calls eval() with TOPLEVEL_BINDING. Therefore you must set local variables in top-level, not inner of Benchmarker.scopde().
File: ex6.rb
require 'benchmarker' s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon" title = "string concat" Benchmarker.scope(title, width: 22, loop: 1000*1000, iter: 5, extra: 1) dos1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon"## empty-loop task is not necessary because overhead of loop is reducedtask nil donilendtask "String#+", <<-'END'dosos = s1 + s2 + s3 + s4 + s5 sos ENDendtask "String#<<", <<-'END'do(sos = "") << s1 << s2 << s3 << s4 << s5 sos ENDendtask "Array#join", <<-'END'dosos = [s1, s2, s3, s4, s5].join() sos ENDendtask "Interpolation", <<-'END'dosos = "#{s1}#{s2}#{s3}#{s4}#{s5}" sos ENDendvalidate do |sos| expected = "HaruhiMikuruYukiItsukiKyon" assert_eq sos, expected ## or assert sos == expected, "expected #{expected.inspect} but got #{sos.inspect}" end end
If you want to refer non-top-level local variables, specify binding() as 3rd argument of task().
require 'benchmarker's1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon"title = "string concat" Benchmarker.scope(title, width: 22, loop: 1000*1000, iter: 5, extra: 1) do s1, s2, s3, s4, s5 = "Haruhi", "Mikuru", "Yuki", "Itsuki", "Kyon" task "String#+", <<-'END', binding() sos = s1 + s2 + s3 + s4 + s5 sos END ....
Advanced topics
Generate sample code
Command-line option -S prints sample code of benchmark script.
$ ruby -r benchmarker -e '' -- -S > mybench.rb $ less mybench.rb $ ruby mybench.rb
Output in JSON format
Command-line -o file.json option will output benchmark data into file.json in JSON format.
$ ruby ex4.py -o result.json ....(snip)... $ less result.json
User-defined global variables
Long options in command-line are regarded as global variables.
For example, command-line option --foo=123 defines $opt_foo = "123"
and --bar defines $opt_bar = true.
require 'benchmarker'
puts "$opt_foo=#{$opt_foo.inspect}"
puts "$opt_bar=#{$opt_bar.inspect}"
Benchmarker.scope() do
....
end
Command-line example:
$ ruby mybench.rb -n 1000 --foo=123 --bar $opt_foo="123" $opt_bar=true ... ## or, use '-s' option of ruby $ ruby -s mybench.rb -opt_foo=123 -opt_bar -- -n 1000 $opt_foo="123" $opt_bar=true ...
Notice that command-line parsing is done when requiring benchmarker.rb.
If you don't parse command-options, define BENCHMARKER_IGNORE_CMDOPTS = true
before requiring benchmarker.rb.
BENCHMARKER_IGNORE_CMDOPTS = true require 'benchmarker'
Skip benchmark tasks
You can skip certain benchmarks by calling skip_if condition, "reason" in benchmark task.
Example:
require 'benchmarker'
begin
require 'active_record'
rescue LoadError
end
Benchmarker.scope("O/R Mapper bench") do |bm|
task "ActiveRecord" do
skip_if !defined?(ActiveRecord), "not installed"
....
end
end
If you want to control skip or not skip slow benchmark tasks:
require 'benchmarker'
skip_slow = ! $opt_all # default value of `! $opt_all` is true
Benchmarker.scope("framework bench") do |bm|
task "Slow benchamrk" do
skip_if skip_slow, "too slow"
....
end
end
$ ruby mybench.rb # skip heavy benchmark tasks $ ruby mybench.rb --all # run all benchmark tasks
Number of times per seconds
If you want to know not only seconds but also number of times per sec (loop / sec), add command-line option -I, or add inverse: true option into Benchmarker.scope().
$ ruby ex2.rb -n 1000000 -I ## title: string concat ## options: loop=1000000, iter=1, extra=0, inverse=true ## benchmarker: release 1.0.0 ## ruby engine: ruby (engine version 2.4.5) ## ruby version: 2.4.5 (patch level 335) ## ruby platform: x86_64-darwin18 ## ruby path: /opt/vs/ruby/2.4.5/bin/ruby ## compiler: Apple LLVM version 10.0.0 (clang-1000.11.45.5) ## os name: Mac OS X 10.14.6 ## cpu model: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz ## user sys total real String#+ 0.5900 0.0100 0.6000 0.5916 String#<< 0.6400 0.0000 0.6400 0.6421 Array#join 0.9300 0.0100 0.9400 0.9497 Interpolation 0.5600 0.0000 0.5600 0.5559 ## Ranking real times/sec Interpolation 0.5559 (100.0%) 1798797.32 String#+ 0.5916 ( 94.0%) 1690457.03 String#<< 0.6421 ( 86.6%) 1557440.75 Array#join 0.9497 ( 58.5%) 1052935.27 ## Matrix real [1] [2] [3] [4] [1] Interpolation 0.5559 100.0% 106.4% 115.5% 170.8% [2] String#+ 0.5916 94.0% 100.0% 108.5% 160.5% [3] String#<< 0.6421 86.6% 92.1% 100.0% 147.9% [4] Array#join 0.9497 58.5% 62.3% 67.6% 100.0%
You may notice that 1798797.32 times/sec is different from 1798884.69 (= 1000000 / 0.5559).
This is because that 0.5559 sec (real time) is rounded value, while 1798797.32 times/sec is calculated from non-rounded value.
In other words, 1798797.32 times/sec is more accurate value than 1798884.69 (= 1000000 / 0.5559).
Command-line option -I can take an optional value, like -I1000.
ruby ex4.rb -n 1000000 -Icalculates1000000 / secfor each result.ruby ex4.rb -n 1000000 -I1000calculates1000 / secfor each result.
Notice that -I 1000 is regarded as -I. Specify argument without space, like -I1000.
Sleep a while after each benchmark task
Benchmarker.scope(sleep: <N>) or command-line option -s <N> makes benchmark to sleep N seconds after each task.
This is intended to avoid thermal runaway of CPU.
### sleep 2 seconds after each benchmark tasks $ ruby mybench.rb -s 2
Refer benchmark options
How to refer benchmark options:
Benchamrker.scope(width: 22, loop: 1000, iter: 10, extra: 1) do |bm| p bm.loop #=> 1000 p bm.iter #=> 10 p bm.extra #=> 1 ....
Compatibility with `benchmark.rb`
Benchmarker provides the followings for compatibility with benchmark.rb (standard library).
BenchmarkmoduleBenchmark.bm()methodBenchmark.bmbm()method
File: ex8.rb
require 'benchmark'require 'benchmarker' nums = (1..10_000_000).to_a Benchmark.bm(20) do |x| # not `Benchmarker` ! x.report "each() & '+='" do total = 0 nums.each {|n| total += n } end x.report "inject()" do total = nums.inject(0) {|t, n| t += n } end x.report "while statement" do total = 0; i = -1; len = nums.length while (i += 1) < len total += nums[i] end end end
See https://ruby-doc.org/stdlib-2.7.0/libdoc/benchmark/rdoc/Benchmark.html
for details of benchamrk.rb.
Command-line options
$ ruby mybench.rb --help # or: ruby -r benchmarker -e '' -- --help Usage: mybench.rb [<options>] -h, --help : help message -v : print Benchmarker version -w <N> : width of task name (default: 30) -n <N> : loop N times in each benchmark (default: 1) -i <N> : iterates all benchmark tasks N times (default: 1) -x <N> : ignore worst N results and best N results (default: 0) -I[<N>] : print inverse number (= N/sec) (default: same as '-n') -o <file> : output file in JSON format -q : quiet a little (suppress output of each iteration) -c : enable colorized output -C : disable colorized output -s <N> : sleep N seconds after each benchmark task -S : print sample code -F task=<...> : filter benchmark task by name (operator: '=' or '!=') -F tag=<...> : filter benchmark task by tag (operator: '=' or '!=') --<key>[=<val>]: define global variable `$opt_<key> = "<val>"`
License and copyright
$License: MIT License $
$Copyright: copyright(c) 2010-2021 kuwata-lab.com all rights reserved. $
Change log
Release 1.0.0
- Public release