benchmark-ips 2.3.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/History.md +228 -0
- data/LICENSE +20 -0
- data/README.md +129 -29
- data/examples/advanced.rb +20 -0
- data/examples/hold.rb +41 -0
- data/examples/save.rb +50 -0
- data/examples/simple.rb +47 -0
- data/lib/benchmark/compare.rb +59 -23
- data/lib/benchmark/ips/job/entry.rb +95 -0
- data/lib/benchmark/ips/job/noop_report.rb +27 -0
- data/lib/benchmark/ips/job/stdout_report.rb +64 -0
- data/lib/benchmark/ips/job.rb +211 -150
- data/lib/benchmark/ips/noop_suite.rb +25 -0
- data/lib/benchmark/ips/report.rb +53 -30
- data/lib/benchmark/ips/share.rb +50 -0
- data/lib/benchmark/ips/stats/bootstrap.rb +58 -0
- data/lib/benchmark/ips/stats/sd.rb +45 -0
- data/lib/benchmark/ips/stats/stats_metric.rb +21 -0
- data/lib/benchmark/ips.rb +91 -24
- data/lib/benchmark/timing.rb +39 -16
- metadata +24 -31
- data/.autotest +0 -23
- data/.gemtest +0 -0
- data/History.txt +0 -87
- data/Manifest.txt +0 -11
- data/Rakefile +0 -26
- data/test/test_benchmark_ips.rb +0 -161
data/lib/benchmark/compare.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Benchmark
|
2
4
|
# Functionality of performaing comparison between reports.
|
3
5
|
#
|
@@ -24,46 +26,80 @@ module Benchmark
|
|
24
26
|
# Reduce using to_proc: 247295.4 i/s - 1.13x slower
|
25
27
|
#
|
26
28
|
# Besides regular Calculating report, this will also indicates which one is slower.
|
29
|
+
#
|
30
|
+
# +x.compare!+ also takes an +order: :baseline+ option.
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# > Benchmark.ips do |x|
|
34
|
+
# x.report('Reduce using block') { [*1..10].reduce { |sum, n| sum + n } }
|
35
|
+
# x.report('Reduce using tag') { [*1..10].reduce(:+) }
|
36
|
+
# x.report('Reduce using to_proc') { [*1..10].reduce(&:+) }
|
37
|
+
# x.compare!(order: :baseline)
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Calculating -------------------------------------
|
41
|
+
# Reduce using block 886.202k (± 2.2%) i/s - 4.521M in 5.103774s
|
42
|
+
# Reduce using tag 1.821M (± 1.6%) i/s - 9.111M in 5.004183s
|
43
|
+
# Reduce using to_proc 895.948k (± 1.6%) i/s - 4.528M in 5.055368s
|
44
|
+
#
|
45
|
+
# Comparison:
|
46
|
+
# Reduce using block: 886202.5 i/s
|
47
|
+
# Reduce using tag: 1821055.0 i/s - 2.05x (± 0.00) faster
|
48
|
+
# Reduce using to_proc: 895948.1 i/s - same-ish: difference falls within error
|
49
|
+
#
|
50
|
+
# The first report is considered the baseline against which other reports are compared.
|
27
51
|
module Compare
|
28
52
|
|
29
53
|
# Compare between reports, prints out facts of each report:
|
30
54
|
# runtime, comparative speed difference.
|
31
|
-
# @param
|
32
|
-
def compare(*
|
33
|
-
return if
|
55
|
+
# @param entries [Array<Report::Entry>] Reports to compare.
|
56
|
+
def compare(*entries, order: :fastest)
|
57
|
+
return if entries.size < 2
|
34
58
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
59
|
+
case order
|
60
|
+
when :baseline
|
61
|
+
baseline = entries.shift
|
62
|
+
sorted = entries.sort_by{ |e| e.stats.central_tendency }.reverse
|
63
|
+
when :fastest
|
64
|
+
sorted = entries.sort_by{ |e| e.stats.central_tendency }.reverse
|
65
|
+
baseline = sorted.shift
|
66
|
+
else
|
67
|
+
raise ArgumentError, "Unknwon order: #{order.inspect}"
|
43
68
|
end
|
44
69
|
|
45
|
-
best = sorted.shift
|
46
|
-
|
47
70
|
$stdout.puts "\nComparison:"
|
48
71
|
|
49
|
-
|
50
|
-
$stdout.printf "%20s: %10.1f i/s\n", best.label, best.ips
|
51
|
-
else
|
52
|
-
$stdout.puts "#{best.rjust(20)}: #{best.runtime}s"
|
53
|
-
end
|
72
|
+
$stdout.printf "%20s: %10.1f i/s\n", baseline.label.to_s, baseline.stats.central_tendency
|
54
73
|
|
55
74
|
sorted.each do |report|
|
56
75
|
name = report.label.to_s
|
57
76
|
|
58
|
-
|
59
|
-
|
60
|
-
|
77
|
+
$stdout.printf "%20s: %10.1f i/s - ", name, report.stats.central_tendency
|
78
|
+
|
79
|
+
if report.stats.overlaps?(baseline.stats)
|
80
|
+
$stdout.print "same-ish: difference falls within error"
|
81
|
+
elsif report.stats.central_tendency > baseline.stats.central_tendency
|
82
|
+
speedup, error = report.stats.speedup(baseline.stats)
|
83
|
+
$stdout.printf "%.2fx ", speedup
|
84
|
+
if error
|
85
|
+
$stdout.printf " (± %.2f)", error
|
86
|
+
end
|
87
|
+
$stdout.print " faster"
|
61
88
|
else
|
62
|
-
|
63
|
-
$stdout.
|
89
|
+
slowdown, error = report.stats.slowdown(baseline.stats)
|
90
|
+
$stdout.printf "%.2fx ", slowdown
|
91
|
+
if error
|
92
|
+
$stdout.printf " (± %.2f)", error
|
93
|
+
end
|
94
|
+
$stdout.print " slower"
|
64
95
|
end
|
96
|
+
|
97
|
+
$stdout.puts
|
65
98
|
end
|
66
99
|
|
100
|
+
footer = baseline.stats.footer
|
101
|
+
$stdout.puts footer.rjust(40) if footer
|
102
|
+
|
67
103
|
$stdout.puts
|
68
104
|
end
|
69
105
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Benchmark
|
2
|
+
module IPS
|
3
|
+
# Benchmark jobs.
|
4
|
+
class Job
|
5
|
+
# Entries in Benchmark Jobs.
|
6
|
+
class Entry
|
7
|
+
# Instantiate the Benchmark::IPS::Job::Entry.
|
8
|
+
# @param label [#to_s] Label of Benchmarked code.
|
9
|
+
# @param action [String, Proc] Code to be benchmarked.
|
10
|
+
# @raise [ArgumentError] Raises when action is not String or not responding to +call+.
|
11
|
+
def initialize(label, action)
|
12
|
+
@label = label
|
13
|
+
|
14
|
+
# We define #call_times on the singleton class of each Entry instance.
|
15
|
+
# That way, there is no polymorphism for `@action.call` inside #call_times.
|
16
|
+
|
17
|
+
if action.kind_of? String
|
18
|
+
compile_string action
|
19
|
+
@action = self
|
20
|
+
else
|
21
|
+
unless action.respond_to? :call
|
22
|
+
raise ArgumentError, "invalid action, must respond to #call"
|
23
|
+
end
|
24
|
+
|
25
|
+
@action = action
|
26
|
+
|
27
|
+
if action.respond_to? :arity and action.arity > 0
|
28
|
+
compile_block_with_manual_loop
|
29
|
+
else
|
30
|
+
compile_block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# The label of benchmarking action.
|
36
|
+
# @return [#to_s] Label of action.
|
37
|
+
attr_reader :label
|
38
|
+
|
39
|
+
# The benchmarking action.
|
40
|
+
# @return [String, Proc] Code to be called, could be String / Proc.
|
41
|
+
attr_reader :action
|
42
|
+
|
43
|
+
# Call action by given times.
|
44
|
+
# @param times [Integer] Times to call +@action+.
|
45
|
+
# @return [Integer] Number of times the +@action+ has been called.
|
46
|
+
def call_times(times)
|
47
|
+
raise '#call_times should be redefined per Benchmark::IPS::Job::Entry instance'
|
48
|
+
end
|
49
|
+
|
50
|
+
def compile_block
|
51
|
+
m = (class << self; self; end)
|
52
|
+
code = <<-CODE
|
53
|
+
def call_times(times)
|
54
|
+
act = @action
|
55
|
+
|
56
|
+
i = 0
|
57
|
+
while i < times
|
58
|
+
act.call
|
59
|
+
i += 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
CODE
|
63
|
+
m.class_eval code
|
64
|
+
end
|
65
|
+
|
66
|
+
def compile_block_with_manual_loop
|
67
|
+
m = (class << self; self; end)
|
68
|
+
code = <<-CODE
|
69
|
+
def call_times(times)
|
70
|
+
@action.call(times)
|
71
|
+
end
|
72
|
+
CODE
|
73
|
+
m.class_eval code
|
74
|
+
end
|
75
|
+
|
76
|
+
# Compile code into +call_times+ method.
|
77
|
+
# @param str [String] Code to be compiled.
|
78
|
+
# @return [Symbol] :call_times.
|
79
|
+
def compile_string(str)
|
80
|
+
m = (class << self; self; end)
|
81
|
+
code = <<-CODE
|
82
|
+
def call_times(__total);
|
83
|
+
__i = 0
|
84
|
+
while __i < __total
|
85
|
+
#{str};
|
86
|
+
__i += 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
CODE
|
90
|
+
m.class_eval code
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Benchmark
|
2
|
+
module IPS
|
3
|
+
class Job
|
4
|
+
class NoopReport
|
5
|
+
def start_warming
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_running
|
9
|
+
end
|
10
|
+
|
11
|
+
def footer
|
12
|
+
end
|
13
|
+
|
14
|
+
def warming(a, b)
|
15
|
+
end
|
16
|
+
|
17
|
+
def warmup_stats(a, b)
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_report(a, b)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :running, :warming
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Benchmark
|
2
|
+
module IPS
|
3
|
+
class Job
|
4
|
+
class StdoutReport
|
5
|
+
def initialize
|
6
|
+
@last_item = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def start_warming
|
10
|
+
$stdout.puts "Warming up --------------------------------------"
|
11
|
+
end
|
12
|
+
|
13
|
+
def start_running
|
14
|
+
$stdout.puts "Calculating -------------------------------------"
|
15
|
+
end
|
16
|
+
|
17
|
+
def warming(label, _warmup)
|
18
|
+
$stdout.print rjust(label)
|
19
|
+
end
|
20
|
+
|
21
|
+
def warmup_stats(_warmup_time_us, timing)
|
22
|
+
case format
|
23
|
+
when :human
|
24
|
+
$stdout.printf "%s i/100ms\n", Helpers.scale(timing)
|
25
|
+
else
|
26
|
+
$stdout.printf "%10d i/100ms\n", timing
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias_method :running, :warming
|
31
|
+
|
32
|
+
def add_report(item, caller)
|
33
|
+
$stdout.puts " #{item.body}"
|
34
|
+
@last_item = item
|
35
|
+
end
|
36
|
+
|
37
|
+
def footer
|
38
|
+
return unless @last_item
|
39
|
+
footer = @last_item.stats.footer
|
40
|
+
$stdout.puts footer.rjust(40) if footer
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [Symbol] format used for benchmarking
|
46
|
+
def format
|
47
|
+
Benchmark::IPS.options[:format]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add padding to label's right if label's length < 20,
|
51
|
+
# Otherwise add a new line and 20 whitespaces.
|
52
|
+
# @return [String] Right justified label.
|
53
|
+
def rjust(label)
|
54
|
+
label = label.to_s
|
55
|
+
if label.size > 20
|
56
|
+
"#{label}\n#{' ' * 20}"
|
57
|
+
else
|
58
|
+
label.rjust(20)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|