bnchmrkr 0.0.1
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 +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +20 -0
- data/README.md +61 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/examples/contrived.rb +15 -0
- data/examples/fibonacci.rb +63 -0
- data/examples/file_reading.rb +90 -0
- data/examples/ls_vs_stat.rb +29 -0
- data/lib/bnchmrkr.rb +187 -0
- data/resources/li-100kw.txt +2179 -0
- data/resources/li-10kw.txt +223 -0
- data/resources/li-1Mw.txt +21791 -0
- data/resources/li-1kw.txt +22 -0
- data/resources/li-500kw.txt +10895 -0
- data/resources/li-50kw.txt +1089 -0
- data/test/unit/test_contrived.rb +78 -0
- data/test/unit/test_initialize.rb +56 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9f1258f5d0cea36bb610c16d36641e2969a8e28
|
4
|
+
data.tar.gz: 3bbc474bdb09cead4a2cd915bf6d4b6ebc1c87a2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5ce962a867982f82026b0a2f4b0ad24f472dee6582e0dd330b5f360e1b2c5b3f1492f48f7bc87b80ce187efa73e1a49c983ab968dfab2848e43cad3461db1f8
|
7
|
+
data.tar.gz: 1db49c32999165c48192499d8d33d43f5e3d298af9adbc33b05cf95b4138ed69b0ae6b859169d40fde6d4412d0f10aae137cd8e5cf01a3164f6e4c6a5b43564f
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
jeweler (0.11.1)
|
5
|
+
rubyforge
|
6
|
+
schacon-git (>= 1.1.1)
|
7
|
+
json_pure (1.8.3)
|
8
|
+
power_assert (0.2.7)
|
9
|
+
rubyforge (2.0.4)
|
10
|
+
json_pure (>= 1.1.7)
|
11
|
+
schacon-git (1.2.2)
|
12
|
+
test-unit (3.0.9)
|
13
|
+
power_assert
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
jeweler (~> 0)
|
20
|
+
test-unit (~> 3.0.0, >= 3.0.0)
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# bnchmrkr
|
2
|
+
|
3
|
+
i hate the name too, [but..](https://github.com/chorankates/bnchmrkr/issues/1)
|
4
|
+
|
5
|
+
Bnchmrkr (Benchmarker) is a tool to help benchmark different method implementations.
|
6
|
+
|
7
|
+
|
8
|
+
# examples
|
9
|
+
|
10
|
+
```rb
|
11
|
+
tester = Bnchmrlr.new({
|
12
|
+
:count_to_1k => lambda { 1.upto(1000).each { |i| i } },
|
13
|
+
:count_to_5k => lambda { 1.upto(5000).each { |i| i } },
|
14
|
+
:count_to_10k => lambda { 1.upto(10000).each { |i| i } },
|
15
|
+
:count_to_50k => lambda { 1.upto(50000).each { |i| i } },
|
16
|
+
:count_to_100k => lambda { 1.upto(100000).each { |i| i } },
|
17
|
+
}, 1000)
|
18
|
+
|
19
|
+
tester.benchmark!
|
20
|
+
|
21
|
+
puts tester
|
22
|
+
```
|
23
|
+
|
24
|
+
```
|
25
|
+
$ ruby examples/ls_vs_stat.rb
|
26
|
+
fastest by type(ls) => 0.005704
|
27
|
+
fastest overall => {:name=>:ls, :measure=>0.005704}
|
28
|
+
slowest by type(stat) => 0.076243
|
29
|
+
slowest_overall => {:name=>:stat, :measure=>0.076243}
|
30
|
+
is_faster?(:ls, :stat) => true
|
31
|
+
is_slower?(:ls, :stat) => false
|
32
|
+
ls:
|
33
|
+
fastest => 0.005704
|
34
|
+
mean => 0.008375
|
35
|
+
median => 0.006715
|
36
|
+
slowest => 0.024187
|
37
|
+
total => 0.083754
|
38
|
+
stat:
|
39
|
+
fastest => 0.061403
|
40
|
+
mean => 0.069956
|
41
|
+
median => 0.073952
|
42
|
+
slowest => 0.076243
|
43
|
+
total => 0.699563
|
44
|
+
overall:
|
45
|
+
fastest => ls [0.005704]
|
46
|
+
slowest => stat [0.076243]
|
47
|
+
```
|
48
|
+
|
49
|
+
# instance methods
|
50
|
+
```
|
51
|
+
benchmark!
|
52
|
+
count
|
53
|
+
faster_by
|
54
|
+
fastest_by_type
|
55
|
+
fastest_overall
|
56
|
+
is_faster?
|
57
|
+
is_slower?
|
58
|
+
results
|
59
|
+
slowest_by_type
|
60
|
+
slowest_overall
|
61
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'jeweler'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
$LOAD_PATH << File.join([File.dirname(__FILE__), 'lib'])
|
5
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
6
|
+
require 'bnchmrkr'
|
7
|
+
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = 'bnchmrkr'
|
10
|
+
gem.summary = 'compare execution time'
|
11
|
+
gem.description = 'given a hash of lambdas, runs and compares the amount of time each implementation takes'
|
12
|
+
gem.email = 'conor.code@gmail.com'
|
13
|
+
gem.homepage = 'http://github.com/chorankates/bnchmrkr'
|
14
|
+
gem.authors = ['Conor Horan-Kates']
|
15
|
+
gem.licenses = 'MIT'
|
16
|
+
|
17
|
+
end
|
18
|
+
Jeweler::RubygemsDotOrgTasks.new
|
19
|
+
|
20
|
+
namespace :test do
|
21
|
+
Rake::TestTask.new do |t|
|
22
|
+
t.name = 'unit'
|
23
|
+
t.libs << 'lib'
|
24
|
+
t.test_files = FileList['test/unit/**/test_*.rb']
|
25
|
+
t.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'run all tests'
|
31
|
+
task :test => ['test:unit'] do
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'run all examples'
|
35
|
+
task :examples do
|
36
|
+
Dir.glob('examples/*.rb').each do |file|
|
37
|
+
sh "time ruby #{file}"
|
38
|
+
puts
|
39
|
+
end
|
40
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# contrived.rb - methods that intentionally take differing amounts of time
|
3
|
+
require_relative File.expand_path(sprintf('%s/../lib/bnchmrkr', File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
tester = Bnchmrkr.new({
|
6
|
+
:count_to_1k => lambda { 1.upto(1000).each { |i| i } },
|
7
|
+
:count_to_5k => lambda { 1.upto(5000).each { |i| i } },
|
8
|
+
:count_to_10k => lambda { 1.upto(10000).each { |i| i } },
|
9
|
+
:count_to_50k => lambda { 1.upto(50000).each { |i| i } },
|
10
|
+
:count_to_100k => lambda { 1.upto(100000).each { |i| i } },
|
11
|
+
}, 1000)
|
12
|
+
|
13
|
+
tester.benchmark!
|
14
|
+
|
15
|
+
puts tester
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# fibonacci.rb - comparing recursive vs. iterative functions
|
3
|
+
|
4
|
+
require_relative File.expand_path(sprintf('%s/../lib/bnchmrkr', File.dirname(__FILE__)))
|
5
|
+
|
6
|
+
require 'test-unit'
|
7
|
+
|
8
|
+
def recursive(n)
|
9
|
+
if n <= 1
|
10
|
+
return n
|
11
|
+
end
|
12
|
+
|
13
|
+
( recursive(n - 1) + recursive(n - 2) )
|
14
|
+
end
|
15
|
+
|
16
|
+
def iterative(target)
|
17
|
+
|
18
|
+
if target <= 1
|
19
|
+
return target
|
20
|
+
end
|
21
|
+
|
22
|
+
hash = {
|
23
|
+
0 => 0,
|
24
|
+
1 => 1,
|
25
|
+
}
|
26
|
+
|
27
|
+
2.upto(target).each do |e|
|
28
|
+
hash[e] = (hash[e - 1] + hash[e - 2])
|
29
|
+
end
|
30
|
+
|
31
|
+
hash[target]
|
32
|
+
end
|
33
|
+
|
34
|
+
class TestFibonacci < Test::Unit::TestCase
|
35
|
+
|
36
|
+
def test_fibonacci_equality
|
37
|
+
|
38
|
+
1.upto(25).each do |i|
|
39
|
+
recursive_result = recursive(i)
|
40
|
+
iterative_result = iterative(i)
|
41
|
+
assert_equal(recursive_result, iterative_result)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_fibonacci_speed
|
47
|
+
|
48
|
+
tester = Bnchmrkr.new({
|
49
|
+
:iterative => lambda { iterative(30) },
|
50
|
+
:recursive => lambda { recursive(30) },
|
51
|
+
}, 25)
|
52
|
+
|
53
|
+
tester.benchmark!
|
54
|
+
puts tester
|
55
|
+
|
56
|
+
assert_true(tester.is_faster?(:iterative, :recursive))
|
57
|
+
assert_true(tester.is_slower?(:recursive, :iterative))
|
58
|
+
assert_equal(:iterative, tester.fastest_overall[:name])
|
59
|
+
assert_equal(:recursive, tester.slowest_overall[:name])
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#
|
2
|
+
# line by line vs. .read and then split
|
3
|
+
|
4
|
+
require_relative File.expand_path(sprintf('%s/../lib/bnchmrkr', File.dirname(__FILE__)))
|
5
|
+
|
6
|
+
class ReadAndSplit
|
7
|
+
attr_reader :file
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
@file = file
|
11
|
+
end
|
12
|
+
|
13
|
+
def read!
|
14
|
+
contents = File.read(@file).split("\n")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ReadAndIterate
|
19
|
+
attr_reader :file
|
20
|
+
|
21
|
+
def initialize(file)
|
22
|
+
@file = file
|
23
|
+
end
|
24
|
+
|
25
|
+
def read!
|
26
|
+
contents = Array.new
|
27
|
+
f = File.new(file)
|
28
|
+
f.each_line do |line|
|
29
|
+
contents << line
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate_ras_variances(files)
|
36
|
+
hash = Hash.new
|
37
|
+
name = :split
|
38
|
+
files.each do |file|
|
39
|
+
local_name = sprintf('%s_%s', name.to_s, File.basename(file)).to_sym
|
40
|
+
hash[local_name] = lambda {
|
41
|
+
brl = ReadAndSplit.new(file)
|
42
|
+
brl.read!
|
43
|
+
}
|
44
|
+
end
|
45
|
+
hash
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_rai_variances(files)
|
49
|
+
hash = Hash.new
|
50
|
+
name = :iterate
|
51
|
+
files.each do |file|
|
52
|
+
local_name = sprintf('%s_%s', name.to_s, File.basename(file)).to_sym
|
53
|
+
hash[local_name] = lambda {
|
54
|
+
rai = ReadAndIterate.new(file)
|
55
|
+
rai.read!
|
56
|
+
}
|
57
|
+
end
|
58
|
+
hash
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
files = [
|
63
|
+
File.expand_path(sprintf('%s/../resources/li-100kw.txt', File.dirname(__FILE__))),
|
64
|
+
File.expand_path(sprintf('%s/../resources/li-500kw.txt', File.dirname(__FILE__))),
|
65
|
+
File.expand_path(sprintf('%s/../resources/li-1Mw.txt', File.dirname(__FILE__))),
|
66
|
+
]
|
67
|
+
|
68
|
+
tester = Bnchmrkr.new({
|
69
|
+
:split => lambda {
|
70
|
+
brl = ReadAndSplit.new(files.first)
|
71
|
+
brl.read!
|
72
|
+
},
|
73
|
+
:iterate => lambda {
|
74
|
+
rai = ReadAndIterate.new(files.first)
|
75
|
+
rai.read!
|
76
|
+
}
|
77
|
+
}, 500)
|
78
|
+
|
79
|
+
tester.benchmark!
|
80
|
+
puts tester
|
81
|
+
|
82
|
+
tester2 = Bnchmrkr.new(
|
83
|
+
generate_rai_variances(files).merge(
|
84
|
+
generate_ras_variances(files)),
|
85
|
+
100)
|
86
|
+
|
87
|
+
tester2.benchmark!
|
88
|
+
puts tester2
|
89
|
+
|
90
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# ls_vs_stat.rb - comparing `ls <dir>/*` to `stat <dir>/*`
|
3
|
+
|
4
|
+
require_relative File.expand_path(sprintf('%s/../lib/bnchmrkr', File.dirname(__FILE__)))
|
5
|
+
|
6
|
+
dir = File.dirname(__FILE__)
|
7
|
+
dir = sprintf('%s/*', ENV['HOME'])
|
8
|
+
|
9
|
+
tester = Bnchmrkr.new({
|
10
|
+
:ls => lambda { `ls #{dir}` },
|
11
|
+
:stat => lambda { `stat #{dir}` },
|
12
|
+
},
|
13
|
+
10,
|
14
|
+
)
|
15
|
+
|
16
|
+
tester.benchmark!
|
17
|
+
|
18
|
+
{
|
19
|
+
'fastest by type(ls)' => tester.fastest_by_type(:ls),
|
20
|
+
'fastest overall' => tester.fastest_overall,
|
21
|
+
'slowest by type(stat)' => tester.slowest_by_type(:stat),
|
22
|
+
'slowest_overall' => tester.slowest_overall,
|
23
|
+
'is_faster?(:ls, :stat)' => tester.is_faster?(:ls, :stat),
|
24
|
+
'is_slower?(:ls, :stat)' => tester.is_slower?(:ls, :stat),
|
25
|
+
}.each_pair do |name, result|
|
26
|
+
puts sprintf(' %#15s => %s%s', name, result, "\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
puts tester
|
data/lib/bnchmrkr.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
class Bnchmrkr
|
6
|
+
|
7
|
+
attr_reader :count, :results
|
8
|
+
|
9
|
+
def initialize(lambdas, count = 100)
|
10
|
+
|
11
|
+
@count = count
|
12
|
+
@results = Hash.new
|
13
|
+
@lambdas = lambdas
|
14
|
+
|
15
|
+
@fastest = { :name => :unknown, :measure => 2 ** 10 } # TODO really need to find a better max int
|
16
|
+
@slowest = { :name => :unknown, :measure => 0 }
|
17
|
+
|
18
|
+
@cache = Hash.new
|
19
|
+
|
20
|
+
@lambdas.each_pair do |name, l|
|
21
|
+
unless name.class.eql?(Symbol) and l.class.eql?(Proc)
|
22
|
+
raise ArgumentError.new(sprintf('expecting[Symbol,Proc], got[%s,%s]', name.class, l.class))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
raise ArgumentError.new(sprintf('expecting[Fixnum], got[%s]', count.class)) unless count.class.eql?(Fixnum)
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# return list of named lambdas known
|
31
|
+
def types
|
32
|
+
@lambdas.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
# 10 lines to actually do the work..
|
36
|
+
def benchmark!
|
37
|
+
@lambdas.each_pair do |name, l|
|
38
|
+
1.upto(@count).each do |round|
|
39
|
+
measure = Benchmark.measure {
|
40
|
+
l.call
|
41
|
+
}
|
42
|
+
add_measure(name, measure)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
## :specific => fastest, slowest, mean, median, total per lambda
|
49
|
+
## :overall => fastest, slowest for all
|
50
|
+
def inspect
|
51
|
+
{ :overall => calculate_overall, :specific => calculate_per_lambda }
|
52
|
+
end
|
53
|
+
|
54
|
+
# overly intricate output formatting of overall and specific results
|
55
|
+
def to_s
|
56
|
+
string = String.new
|
57
|
+
inspection = self.inspect
|
58
|
+
return string unless inspection.nil? or inspection.has_key?(:overall)
|
59
|
+
longest_key = 15 # TODO determine this dynamically
|
60
|
+
|
61
|
+
inspection[:specific].keys.each do |i|
|
62
|
+
string << sprintf('%s:%s', i, "\n")
|
63
|
+
inspection[:specific][i].keys.sort.each do |k|
|
64
|
+
string << sprintf(" %#{longest_key}s => %s%s", k, inspection[:specific][i][k], "\n")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
string << sprintf('overall:%s', "\n")
|
69
|
+
inspection[:overall].each_pair do |type, measure|
|
70
|
+
string << sprintf(" %#{longest_key}s => %s [%s]%s", type, measure[:name], measure[:measure], "\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
string
|
74
|
+
end
|
75
|
+
|
76
|
+
# +type+ name of a lambda that is known
|
77
|
+
# find and return the fastest execution per lambda of +type+
|
78
|
+
def fastest_by_type(type)
|
79
|
+
results = @results
|
80
|
+
measures = Array.new
|
81
|
+
|
82
|
+
return nil unless results.has_key?(type)
|
83
|
+
results[type].collect { |r| measures << r.real}
|
84
|
+
|
85
|
+
measures.sort.first
|
86
|
+
end
|
87
|
+
|
88
|
+
# find and return the fastest overall execution (regardless of lambda type)
|
89
|
+
def fastest_overall
|
90
|
+
calculate_overall
|
91
|
+
@fastest
|
92
|
+
end
|
93
|
+
|
94
|
+
# +type+ name of a lambda that is known
|
95
|
+
# find and return the slowest execution per lambda of +type+
|
96
|
+
def slowest_by_type(type)
|
97
|
+
results = @results
|
98
|
+
measures = Array.new
|
99
|
+
|
100
|
+
return nil unless results.has_key?(type)
|
101
|
+
results[type].collect { |r| measures << r.real }
|
102
|
+
|
103
|
+
measures.sort.last
|
104
|
+
end
|
105
|
+
|
106
|
+
# find and return the slowest overall execution (regardless of lambda type)
|
107
|
+
def slowest_overall
|
108
|
+
calculate_overall
|
109
|
+
@slowest
|
110
|
+
end
|
111
|
+
|
112
|
+
# +a+ {:name => name, :measure => measure}
|
113
|
+
# +b+ {:name => name, :measure => measure}
|
114
|
+
# +mode+ :fastest, :slowest, :mean, :median, :total
|
115
|
+
# return boolean if a is faster than b, false if invalid
|
116
|
+
def is_faster?(a, b, mode = :total)
|
117
|
+
result = calculate_per_lambda
|
118
|
+
return false unless result.has_key?(a[:name]) and result.has_key?(b[:name])
|
119
|
+
result[a[:name]][mode] < result[b[:name]][mode]
|
120
|
+
end
|
121
|
+
|
122
|
+
# +a+ {:name => name, :measure => measure}
|
123
|
+
# +b+ {:name => name, :measure => measure}
|
124
|
+
# +mode+ :fastest, :slowest, :mean, :median, :total
|
125
|
+
# return boolean if a is faster than b, false if invalid
|
126
|
+
def is_slower?(a, b, mode = :total)
|
127
|
+
! is_faster?(a, b, mode)
|
128
|
+
end
|
129
|
+
|
130
|
+
# +a+ {:name => name, :measure => measure}
|
131
|
+
# +b+ {:name => name, :measure => measure}
|
132
|
+
# return Float representing difference in measure, or false, if b is slower than a
|
133
|
+
def faster_by(a, b, percent = true)
|
134
|
+
return false if b[:measure] < a[:measure]
|
135
|
+
|
136
|
+
faster = (b[:measure] - a[:measure]) / b[:measure]
|
137
|
+
percent ? sprintf('%4f%', faster * 100) : faster
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def add_measure(name, measure)
|
143
|
+
self.results[name] = Array.new unless self.results.has_key?(name)
|
144
|
+
self.results[name] << measure
|
145
|
+
end
|
146
|
+
|
147
|
+
# from existing results, generate some statistics per lambda type
|
148
|
+
def calculate_per_lambda
|
149
|
+
hash = Hash.new
|
150
|
+
|
151
|
+
# TODO come up with way to not recompute unless contents have changed
|
152
|
+
|
153
|
+
@results.each_pair do |name, measures|
|
154
|
+
sorted = measures.sort { |a,b| a.real <=> b.real }
|
155
|
+
|
156
|
+
hash[name] = Hash.new
|
157
|
+
|
158
|
+
total = 0
|
159
|
+
measures.collect {|m| total += m.real }
|
160
|
+
|
161
|
+
# TODO do we want to determine the mode?
|
162
|
+
hash[name][:fastest] = sorted.first.real
|
163
|
+
hash[name][:slowest] = sorted.last.real
|
164
|
+
hash[name][:mean] = sprintf('%5f', total / sorted.size)
|
165
|
+
hash[name][:median] = sorted[(sorted.size / 2)].real
|
166
|
+
hash[name][:total] = sprintf('%5f', total)
|
167
|
+
end
|
168
|
+
|
169
|
+
hash
|
170
|
+
end
|
171
|
+
|
172
|
+
# update fastest/slowest, return in a named Hash
|
173
|
+
def calculate_overall
|
174
|
+
calculate_per_lambda.each_pair do |name,results|
|
175
|
+
if results[:fastest] < @fastest[:measure]
|
176
|
+
@fastest = { :name => name, :measure => results[:fastest] }
|
177
|
+
end
|
178
|
+
|
179
|
+
if results[:slowest] > @slowest[:measure]
|
180
|
+
@slowest = { :name => name, :measure => results[:slowest] }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
{ :fastest => @fastest, :slowest => @slowest }
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|