benchmark-sweet 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +246 -0
- data/Rakefile +6 -0
- data/benchmark-sweet.gemspec +48 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/benchmark_big_small.rb +84 -0
- data/examples/benchmark_big_split.rb +49 -0
- data/examples/benchmark_blank.rb +56 -0
- data/examples/benchmark_rpt_database.rb +127 -0
- data/examples/benchmark_simple_database.rb +34 -0
- data/lib/benchmark-sweet.rb +1 -0
- data/lib/benchmark/sweet.rb +94 -0
- data/lib/benchmark/sweet/comparison.rb +101 -0
- data/lib/benchmark/sweet/ips.rb +16 -0
- data/lib/benchmark/sweet/item.rb +30 -0
- data/lib/benchmark/sweet/job.rb +244 -0
- data/lib/benchmark/sweet/memory.rb +41 -0
- data/lib/benchmark/sweet/queries.rb +60 -0
- data/lib/benchmark/sweet/version.rb +5 -0
- metadata +189 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'benchmark/sweet'
|
2
|
+
require 'active_support/all'
|
3
|
+
require "more_core_extensions/all" # [].tabelize
|
4
|
+
|
5
|
+
# version 2.3.7
|
6
|
+
#
|
7
|
+
# method | nil_ips | str_ips | nil_memsize | str_memsize
|
8
|
+
# -------------+------------------------+---------------+--------------------+-------------
|
9
|
+
# ?split:[] | 51825798.8 i/s | 1407946.4 i/s | 40.0 bytes | 360.0 bytes
|
10
|
+
# &&split||[] | 46730725.8 i/s - 1.11x | 1413355.3 i/s | 40.0 bytes | 360.0 bytes
|
11
|
+
# to_s.split | 5685237.1 i/s - 9.12x | 1396494.3 i/s | 80.0 bytes - 2.00x | 360.0 bytes
|
12
|
+
#
|
13
|
+
# version 2.4.4
|
14
|
+
#
|
15
|
+
# method | nil_ips | str_ips | nil_memsize | str_memsize
|
16
|
+
# -------------+------------------------+---------------+--------------------+-------------
|
17
|
+
# ?split:[] | 51559454.4 i/s | 1438780.8 i/s | 40.0 bytes | 360.0 bytes
|
18
|
+
# &.split||[] | 46446196.0 i/s - 1.11x | 1437665.3 i/s | 40.0 bytes | 360.0 bytes
|
19
|
+
# &&split||[] | 43356335.6 i/s - 1.19x | 1434466.6 i/s | 40.0 bytes | 360.0 bytes
|
20
|
+
# to_s.split | 5835694.7 i/s - 8.84x | 1427819.0 i/s | 80.0 bytes - 2.00x | 360.0 bytes
|
21
|
+
|
22
|
+
NSTRING = nil
|
23
|
+
DELIMITER = '/'.freeze
|
24
|
+
STRING = "ab/cd/ef/gh".freeze
|
25
|
+
|
26
|
+
Benchmark.items(metrics: %w(ips memsize), memory: 3, warmup: 1, time: 3, quiet: false, force: ENV["FORCE"] == "true") do |x|
|
27
|
+
x.metadata version: RUBY_VERSION
|
28
|
+
x.metadata data: "nil" do
|
29
|
+
x.report("to_s.split", "NSTRING.to_s.split(DELIMITER)")
|
30
|
+
x.report("?split:[]", "NSTRING ? NSTRING.split(DELIMITER) : []")
|
31
|
+
x.report("&&split||[]", "NSTRING && NSTRING.split(DELIMITER) || []")
|
32
|
+
x.report("&.split||[]", "NSTRING&.split(DELIMITER) || []") if RUBY_VERSION >= "2.4"
|
33
|
+
end
|
34
|
+
|
35
|
+
x.metadata data: "str" do
|
36
|
+
x.report("to_s.split", "STRING.to_s.split(DELIMITER)")
|
37
|
+
x.report("?split:[]", "STRING ? STRING.split(DELIMITER) : []")
|
38
|
+
x.report("&&split||[]", "STRING && STRING.split(DELIMITER) || []")
|
39
|
+
x.report("&.split||[]", "STRING&.split(DELIMITER) || []") if RUBY_VERSION >= "2.4"
|
40
|
+
end
|
41
|
+
|
42
|
+
# partition the data by ruby version and whether data is present
|
43
|
+
# that way we're only comparing similar values
|
44
|
+
# note: this is not necessarily the correlation to how the data is displayed
|
45
|
+
x.compare_by :version, :data
|
46
|
+
x.report_with grouping: [:metric, :version], row: :method, column: [:data], value: :comp_short
|
47
|
+
|
48
|
+
x.save_file (ENV["SAVE_FILE"] == "true") ? $0.sub(/\.rb$/, '.json') : ENV["SAVE_FILE"] if ENV["SAVE_FILE"]
|
49
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "benchmark/sweet"
|
2
|
+
require "active_support/all"
|
3
|
+
require "more_core_extensions/all" # [].tabelize
|
4
|
+
|
5
|
+
# method | NIL | EMPTY | FULL
|
6
|
+
# --------------------+------------------------+------------------------+------------------------
|
7
|
+
# x&.empty? | 13752904.8 i/s | 13089322.9 i/s | 13352488.3 i/s
|
8
|
+
# !x && x.empty? | 13334422.1 i/s | 12019275.5 i/s - 1.14x | 11882260.1 i/s - 1.16x
|
9
|
+
# x.blank? | 11889050.9 i/s - 1.16x | 11673162.3 i/s - 1.18x | 12039255.2 i/s - 1.14x
|
10
|
+
# x.nil? || x.empty? | 11620573.1 i/s - 1.18x | 10676420.2 i/s - 1.29x | 10048254.7 i/s - 1.37x
|
11
|
+
# x.try!(:empty) | 6240643.7 i/s - 2.20x | 3962583.3 i/s - 3.47x | 4071200.6 i/s - 3.38x
|
12
|
+
# x.try(:empty) | 6044172.3 i/s - 2.28x | 2385145.0 i/s - 5.76x | 2454406.3 i/s - 5.60x
|
13
|
+
# x.empty? | | 13743969.3 i/s | 13754672.4 i/s
|
14
|
+
|
15
|
+
ANIL=nil
|
16
|
+
EMPTY=[].freeze
|
17
|
+
FULL=["a"].freeze
|
18
|
+
|
19
|
+
Benchmark.items(metrics: %w(ips)) do |x|
|
20
|
+
x.metadata version: RUBY_VERSION
|
21
|
+
x.metadata data: 'NIL' do
|
22
|
+
x.report("x.nil? || x.empty?") { ANIL.nil? || ANIL.empty? }
|
23
|
+
x.report("!x && x.empty?") { !ANIL || ANIL.empty? }
|
24
|
+
x.report("x&.empty?") { ANIL&.empty? }
|
25
|
+
x.report("x.try!(:empty)") { ANIL.try!(:empty?) }
|
26
|
+
x.report("x.try(:empty)") { ANIL.try(:empty?) }
|
27
|
+
x.report("x.blank?") { ANIL.blank? }
|
28
|
+
end
|
29
|
+
|
30
|
+
x.metadata data: 'EMPTY' do
|
31
|
+
x.report("x.nil? || x.empty?") { EMPTY.nil? || EMPTY.empty? }
|
32
|
+
x.report("!x && x.empty?") { !EMPTY || EMPTY.empty? }
|
33
|
+
x.report("x&.empty?") { EMPTY&.empty? }
|
34
|
+
x.report("x.try!(:empty)") { EMPTY.try!(:empty?) }
|
35
|
+
x.report("x.try(:empty)") { EMPTY.try(:empty?) }
|
36
|
+
x.report("x.blank?") { EMPTY.blank? }
|
37
|
+
# base case
|
38
|
+
x.report("x.empty?") { EMPTY.empty? }
|
39
|
+
end
|
40
|
+
|
41
|
+
x.metadata data: 'FULL' do
|
42
|
+
x.report("x.nil? || x.empty?") { FULL.nil? || FULL.empty? }
|
43
|
+
x.report("!x && x.empty?") { !FULL || FULL.empty? }
|
44
|
+
x.report("x&.empty?") { FULL&.empty? }
|
45
|
+
x.report("x.try!(:empty)") { FULL.try!(:empty?) }
|
46
|
+
x.report("x.try(:empty)") { FULL.try(:empty?) }
|
47
|
+
x.report("x.blank?") { FULL.blank? }
|
48
|
+
# base case
|
49
|
+
x.report("x.empty?") { FULL.empty? }
|
50
|
+
end
|
51
|
+
|
52
|
+
x.compare_by :data
|
53
|
+
x.report_with grouping: nil, row: :method, column: :data
|
54
|
+
|
55
|
+
x.save_file (ENV["SAVE_FILE"] == "true") ? $0.sub(/\.rb$/, '.json') : ENV["SAVE_FILE"] if ENV["SAVE_FILE"]
|
56
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require "benchmark/sweet"
|
2
|
+
require "more_core_extensions/all"
|
3
|
+
require "active_record"
|
4
|
+
|
5
|
+
# For various versions of rails, compare `Model.all.first` vs `Model.all.to_a.first`
|
6
|
+
#
|
7
|
+
# To work across versions, we need to run this multiple times (with different Gemfile each run)
|
8
|
+
#
|
9
|
+
# meta data of :version is stored to be able to distinguish the version across each of the runs
|
10
|
+
#
|
11
|
+
# options:
|
12
|
+
# DATABASE_URL
|
13
|
+
# link to the database
|
14
|
+
#
|
15
|
+
# DATABASE_URL=postgres://user@password:localhost/user_benchmark
|
16
|
+
#
|
17
|
+
# SAVE_FILE
|
18
|
+
# a save file contains the values from the various invocations.
|
19
|
+
# This uses the method `save_file`, which is optional. But without it, it won't compare across versions.
|
20
|
+
#
|
21
|
+
# running this script multiple times will only use the first value obtained
|
22
|
+
# But running this with different metadata (i.e. AR version) will run again and compare across the versions
|
23
|
+
#
|
24
|
+
# default : write to a save file named "{this script's name}.json"
|
25
|
+
# SAVE_FILE=file.json : write to a save file named "file.json"
|
26
|
+
#
|
27
|
+
# FORCE
|
28
|
+
# This tells the script to overwrite previously run identical metadata
|
29
|
+
# It uses the `force: true` option to share with benchmark that this behavior is desired
|
30
|
+
#
|
31
|
+
# FORCE=true :overwrite the previous values for this script
|
32
|
+
# FORCE=false : default behavior. don't run multiple times for the same metadata
|
33
|
+
#
|
34
|
+
|
35
|
+
# version 5.2.1
|
36
|
+
#
|
37
|
+
# grouping 6.0.2.2 (100 records)
|
38
|
+
#
|
39
|
+
# method | ips | memsize
|
40
|
+
# ------------+--------------------+---------------------
|
41
|
+
# first | 2945.9 i/s | 9808 bytes
|
42
|
+
# to_a.first | 1204.7 i/s - 2.45x | 68200 bytes - 6.95x
|
43
|
+
#
|
44
|
+
# NOTE: the results are in color
|
45
|
+
|
46
|
+
ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL') { "postgres://localhost/user_benchmark" })
|
47
|
+
ActiveRecord::Migration.verbose = false
|
48
|
+
|
49
|
+
ActiveRecord::Schema.define do
|
50
|
+
create_table :users, force: true do |t|
|
51
|
+
t.string :name
|
52
|
+
end
|
53
|
+
#add_index :users, :name
|
54
|
+
|
55
|
+
create_table :accounts, force: true do |t|
|
56
|
+
t.string :name
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class User < ActiveRecord::Base; end
|
61
|
+
class Account < ActiveRecord::Base; end
|
62
|
+
|
63
|
+
if User.count == 0
|
64
|
+
puts "Creating 100 users"
|
65
|
+
100.times { |i| User.create name: "user #{i}" }
|
66
|
+
end
|
67
|
+
|
68
|
+
#in the table cells, it typically displays the value and units. this lambda is adding in colors (based upon best/worst)
|
69
|
+
VALUE_TO_S = ->(m) { m.comp_short("\e[#{m.color}m#{m.central_tendency.round(1)} #{m.units}\e[0m") }
|
70
|
+
|
71
|
+
# These are the various items that will be compared
|
72
|
+
#
|
73
|
+
# metrics is the metric that is actually run
|
74
|
+
# memory
|
75
|
+
# warmup - run the tests for this many seconds before running the actual benchmark (for ips)
|
76
|
+
# time - run the tests for this many seconds (for ips)
|
77
|
+
# quied - display the ips run information
|
78
|
+
# force - defined above. this is allowing the command line to change this value (default: false)
|
79
|
+
Benchmark.items(metrics: %w(ips memsize), memory: 3, warmup: 1, time: 3, quiet: false, force: ENV["FORCE"] == "true") do |x|
|
80
|
+
# for all examples, store this metadata with the row
|
81
|
+
# metadata is stored in the savefile along with the method name and benchmarks.
|
82
|
+
# future runs of the script that have a different version of AR will be stored as separate benchmarks
|
83
|
+
# this allows comparison across multiple versions
|
84
|
+
#
|
85
|
+
# If you are applying a patch for different behavior, or you're running against head, consider using something more comples:
|
86
|
+
#
|
87
|
+
# this can be an array. with something like version: [ActiveRecord.version.to_s, ENV["PATCH"], ENV["SHA"]].compact.join(".")
|
88
|
+
x.metadata version: ActiveRecord.version.to_s
|
89
|
+
|
90
|
+
# compare_by is a display parameter
|
91
|
+
#
|
92
|
+
# this is used to know which metadata is different and which are the same
|
93
|
+
# the best and worst value is determined across this criteria
|
94
|
+
#
|
95
|
+
# so if you want to show a different list for each version, add it to the list
|
96
|
+
# This typically will include the group and either the column or the row header
|
97
|
+
#
|
98
|
+
# defaults to unique by method (and always unique by metric)
|
99
|
+
x.compare_by { |label, _| [label[:count], label[:version]] }
|
100
|
+
|
101
|
+
# these next two cases are marked with count=100. Use row, column or grouping to group these
|
102
|
+
# These values tend to be in the compare_by block. because running on 100 values is different than 0. (but not always the case)
|
103
|
+
x.metadata count: "100" do
|
104
|
+
x.report("first") { User.all.first }
|
105
|
+
x.report("to_a.first") { User.all.to_a.first }
|
106
|
+
end
|
107
|
+
|
108
|
+
x.metadata count: "0" do
|
109
|
+
x.report("first") { Account.all.first }
|
110
|
+
x.report("to_a.first") { Account.all.to_a.first }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Note, often the report title is the same as the code, in those cases just pass the name
|
114
|
+
# x.report("Account.all.to_a.first")
|
115
|
+
# x.report("Account.all.to_a.first")
|
116
|
+
|
117
|
+
# display only. parameters
|
118
|
+
# these can be symbols or lambdas
|
119
|
+
# grouping is the value for determining what data goes into each table (default - only 1 table)
|
120
|
+
# row is the title on the left hand side (default: all metadata)
|
121
|
+
# column header is the metric that was captured (i.e.: ips or metrics) (default: metric name)
|
122
|
+
# value is the text that is displayed in the cell of the table (default: "central_tendancy units")
|
123
|
+
x.report_with grouping: ->(l){ "#{l[:version]} (#{l[:count]} records)"}, row: :method, column: :metric, value: VALUE_TO_S
|
124
|
+
|
125
|
+
# defined above. benchmark sweet pretty much depends upon this json file
|
126
|
+
x.save_file ENV["SAVE_FILE"] ? ENV["SAVE_FILE"] : $0.sub(/\.rb$/, '.json')
|
127
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "benchmark/sweet"
|
2
|
+
require "more_core_extensions/all"
|
3
|
+
require "active_record"
|
4
|
+
#
|
5
|
+
# label | ips | queries | rows
|
6
|
+
#---------------------+-------------------+----------+----------------------
|
7
|
+
# User.all.first | 3185.3 i/s | 1.0 objs | 1.0 objs
|
8
|
+
# User.all.to_a.first | 977.1 i/s - 3.26x | 1.0 objs | 100.0 objs - 100.00x
|
9
|
+
|
10
|
+
|
11
|
+
ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL') { "postgres://localhost/user_benchmark" })
|
12
|
+
ActiveRecord::Migration.verbose = false
|
13
|
+
|
14
|
+
ActiveRecord::Schema.define do
|
15
|
+
create_table :users, force: true do |t|
|
16
|
+
t.string :name
|
17
|
+
end
|
18
|
+
#add_index :users, :name
|
19
|
+
end
|
20
|
+
|
21
|
+
class User < ActiveRecord::Base ;end
|
22
|
+
|
23
|
+
if User.count == 0
|
24
|
+
puts "Creating 100 users"
|
25
|
+
100.times { |i| User.create name: "user #{i}" }
|
26
|
+
end
|
27
|
+
|
28
|
+
Benchmark.items(metrics: %w(ips queries rows)) do |x|
|
29
|
+
x.report("User.all.first")
|
30
|
+
x.report("User.all.to_a.first")
|
31
|
+
|
32
|
+
x.report_with row: :method, column: :metric
|
33
|
+
x.save_file (ENV["SAVE_FILE"] == "true") ? $0.sub(/\.rb$/, '.json') : ENV["SAVE_FILE"] if ENV["SAVE_FILE"]
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "benchmark/sweet"
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "benchmark/sweet/version"
|
2
|
+
require "benchmark/sweet/ips"
|
3
|
+
require "benchmark/sweet/memory"
|
4
|
+
require "benchmark/sweet/queries"
|
5
|
+
require "benchmark/sweet/job"
|
6
|
+
require "benchmark/sweet/comparison"
|
7
|
+
require "benchmark/sweet/item"
|
8
|
+
require "benchmark/ips"
|
9
|
+
|
10
|
+
module Benchmark
|
11
|
+
module Sweet
|
12
|
+
def items(options = {memory: true, ips: true})
|
13
|
+
job = ::Benchmark::Sweet::Job.new(options)
|
14
|
+
|
15
|
+
yield job
|
16
|
+
|
17
|
+
job.load_entries
|
18
|
+
job.run
|
19
|
+
job.save_entries
|
20
|
+
|
21
|
+
job.run_report
|
22
|
+
|
23
|
+
job # both items and entries are useful
|
24
|
+
end
|
25
|
+
|
26
|
+
# report helper methods
|
27
|
+
# these are the building blocks for the reports printed.
|
28
|
+
# These can be used to create different tables
|
29
|
+
|
30
|
+
# @param base [Array<Comparison>}] array of comparisons
|
31
|
+
# @param grouping [Symbol|Array<Symbol>|Proc] Proc passed to group_by to partition records.
|
32
|
+
# Accepts Comparison, returns an object to partition. returns nil to filter from the list
|
33
|
+
# @keyword sort [Boolean] true to sort by the grouping value (default false)
|
34
|
+
# Proc accepts the label to generate custom summary text
|
35
|
+
def self.group(base, grouping, sort: false, &block)
|
36
|
+
if grouping.nil?
|
37
|
+
yield nil, base
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
grouping = symbol_to_proc(grouping)
|
42
|
+
|
43
|
+
label_records = base.group_by(&grouping).select { |value, comparisons| !value.nil? }
|
44
|
+
label_records = label_records.sort_by(&:first) if sort
|
45
|
+
|
46
|
+
label_records.each(&block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.table(base, grouping: nil, sort: false, row: :label, column: :metric, value: :comp_short)
|
50
|
+
header_name = grouping.respond_to?(:call) ? "grouping" : grouping
|
51
|
+
column = symbol_to_proc(column)
|
52
|
+
value = symbol_to_proc(value)
|
53
|
+
|
54
|
+
group(base, grouping, sort: true) do |header_value, table_comparisons|
|
55
|
+
row_key = row.kind_of?(Symbol) || row.kind_of?(String) ? row : "label"
|
56
|
+
table_rows = group(table_comparisons, row, sort: sort).map do |row_header, row_comparisons|
|
57
|
+
row_comparisons.each_with_object({row_key => row_header}) do |comparison, row_data|
|
58
|
+
row_data[column.call(comparison)] = value.call(comparison)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if block_given?
|
62
|
+
yield header_value, table_rows
|
63
|
+
else
|
64
|
+
print_table(header_name, header_value, table_rows)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# proc produced: -> (comparison) { comparison.method }
|
70
|
+
def self.symbol_to_proc(field, join: "_")
|
71
|
+
if field.kind_of?(Symbol) || field.kind_of?(String)
|
72
|
+
field_name = field
|
73
|
+
-> v { v[field_name] }
|
74
|
+
elsif field.kind_of?(Array)
|
75
|
+
field_names = field
|
76
|
+
if join
|
77
|
+
-> v { field_names.map { |gn| v[gn].to_s }.join(join) }
|
78
|
+
else
|
79
|
+
-> v { field_names.map { |gn| v[gn] } }
|
80
|
+
end
|
81
|
+
else
|
82
|
+
field
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.print_table(header_name, header_value, table_rows)
|
87
|
+
require "more_core_extensions" # defines tableize
|
88
|
+
puts "", "#{header_name} #{header_value}", "" if header_value
|
89
|
+
# passing colummns to make sure table keeps the same column order
|
90
|
+
puts table_rows.tableize(:columns => table_rows.first.keys)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
extend Benchmark::Sweet
|
94
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Benchmark
|
2
|
+
module Sweet
|
3
|
+
class Comparison
|
4
|
+
UNITS = {"ips" => "i/s", "memsize" => "bytes", "memsize_retained" => "bytes"}.freeze
|
5
|
+
attr_reader :label, :metric, :stats, :baseline, :worst
|
6
|
+
attr_reader :offset, :total
|
7
|
+
def initialize(metric, label, stats, offset, total, baseline, worst = nil)
|
8
|
+
@metric = metric
|
9
|
+
@label = label
|
10
|
+
@stats = stats
|
11
|
+
@offset = offset
|
12
|
+
@total = total
|
13
|
+
@baseline = baseline
|
14
|
+
@worst = worst
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](field)
|
18
|
+
case field
|
19
|
+
when :metric then metric
|
20
|
+
when :comp_short then comp_short
|
21
|
+
when :comp_string then comp_string
|
22
|
+
when :label then label # not sure if this one makes sense
|
23
|
+
else label[field]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def central_tendency ; stats.central_tendency ; end
|
28
|
+
def error ; stats.error ; end
|
29
|
+
def units ; UNITS[metric] || "objs" ; end
|
30
|
+
|
31
|
+
def mode
|
32
|
+
@mode ||= best? ? :best : overlaps? ? :same : diff_error ? :slowerish : :slower
|
33
|
+
end
|
34
|
+
|
35
|
+
def best? ; !baseline || (baseline == stats) ; end
|
36
|
+
|
37
|
+
# @return true if it is basically the same as the best
|
38
|
+
def overlaps?
|
39
|
+
return @overlaps if defined?(@overlaps)
|
40
|
+
@overlaps = slowdown == 1 ||
|
41
|
+
stats && baseline && (stats.central_tendency == baseline.central_tendency || stats.overlaps?(baseline))
|
42
|
+
end
|
43
|
+
|
44
|
+
def worst?
|
45
|
+
@worst ? stats.overlaps?(@worst) : (total.to_i - 1 == offset.to_i) && slowdown.to_i > 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def slowdown
|
49
|
+
return @slowdown if @slowdown
|
50
|
+
@slowdown, @diff_error = stats.slowdown(baseline)
|
51
|
+
@slowdown
|
52
|
+
end
|
53
|
+
|
54
|
+
def diff_error
|
55
|
+
@diff_error ||= (slowdown ; @diff_error)
|
56
|
+
end
|
57
|
+
|
58
|
+
# quick display
|
59
|
+
|
60
|
+
def comp_string(l_to_s = nil)
|
61
|
+
l_to_s ||= -> l { l.to_s }
|
62
|
+
case mode
|
63
|
+
when :best
|
64
|
+
"%20s: %10.1f %s" % [l_to_s.call(label), central_tendency, units]
|
65
|
+
when :same
|
66
|
+
"%20s: %10.1f %s - same-ish: difference falls within error" % [l_to_s.call(label), central_tendency, units]
|
67
|
+
when :slower
|
68
|
+
"%20s: %10.1f %s - %.2fx (± %.2f) slower" % [l_to_s.call(label), central_tendency, units, slowdown, error]
|
69
|
+
when :slowerish
|
70
|
+
"%20s: %10.1f %s - %.2fx slower" % [l_to_s.call(label), central_tendency, units, slowdown]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# I tend to call with:
|
75
|
+
# c.comp_short("\033[#{c.color}m#{c.central_tendency.round(1)} #{c.units}\e[0m") # "\033[31m#{value}\e[0m"
|
76
|
+
def comp_short(value = nil)
|
77
|
+
value ||= "#{central_tendency.round(1)} #{units}"
|
78
|
+
case mode
|
79
|
+
when :best, :same
|
80
|
+
value
|
81
|
+
when :slower
|
82
|
+
"%s - %.2fx (± %.2f)" % [value, slowdown, error]
|
83
|
+
when :slowerish
|
84
|
+
"%s - %.2fx" % [value, slowdown]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def color
|
89
|
+
if !baseline
|
90
|
+
";0"
|
91
|
+
elsif best? || overlaps?
|
92
|
+
"32"
|
93
|
+
elsif worst?
|
94
|
+
"31"
|
95
|
+
else
|
96
|
+
";0"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|