benchmark-ips 2.3.0 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|