benchmark-trend 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +13 -3
- data/lib/benchmark-trend.rb +1 -1
- data/lib/benchmark/trend.rb +3 -29
- data/lib/benchmark/trend/clock.rb +67 -0
- data/lib/benchmark/trend/version.rb +1 -1
- metadata +18 -37
- data/Rakefile +0 -8
- data/benchmark-trend.gemspec +0 -28
- data/spec/spec_helper.rb +0 -31
- data/spec/unit/fit_at_spec.rb +0 -39
- data/spec/unit/fit_exp_spec.rb +0 -26
- data/spec/unit/fit_linear_spec.rb +0 -56
- data/spec/unit/fit_log_spec.rb +0 -26
- data/spec/unit/fit_power_spec.rb +0 -26
- data/spec/unit/format_fit_spec.rb +0 -29
- data/spec/unit/infer_trend_spec.rb +0 -97
- data/spec/unit/measure_execution_time_spec.rb +0 -19
- data/spec/unit/range_spec.rb +0 -31
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6729682c2148955fb9411b540070d10a51d3dd2d0ab6907fcedc909c250da34f
|
4
|
+
data.tar.gz: 391408701a394ddf0d61561ddc27d367a82b77a46c61e2d244ec4f1f8f444bd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14612b7218f12783d8b7cb935a04cd1e9146361faa4a0004b4b221c02e0215c63dcdfb235298a49375f39096b95a15efcf8503660452469ad1bde9cf9f455eb2
|
7
|
+
data.tar.gz: dc5a88b5536c9879c2f893e9710980926aae76d8af9c4ae47322727753eac59df9bb4614be6f1d7761f0cbb5b3d4141b0eb89862cbea823bc67248a35fd5147a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## [v0.4.0] - 2020-03-07
|
4
|
+
|
5
|
+
### Added
|
6
|
+
* Add Clock for monotonic time measuring
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
* Change to remove benchmark requirement
|
10
|
+
* Change gemspec to add metadata, remove test artefacts
|
11
|
+
|
3
12
|
## [v0.3.0] - 2019-04-21
|
4
13
|
|
5
14
|
### Changed
|
@@ -25,6 +34,7 @@
|
|
25
34
|
|
26
35
|
* Initial implementation and release
|
27
36
|
|
37
|
+
[v0.4.0]: https://github.com/piotrmurach/benchmark-trend/compare/v0.3.0...v0.4.0
|
28
38
|
[v0.3.0]: https://github.com/piotrmurach/benchmark-trend/compare/v0.2.0...v0.3.0
|
29
39
|
[v0.2.0]: https://github.com/piotrmurach/benchmark-trend/compare/v0.1.0...v0.2.0
|
30
40
|
[v0.1.0]: https://github.com/piotrmurach/benchmark-trend/compare/v0.1.0
|
data/README.md
CHANGED
@@ -256,9 +256,19 @@ print error
|
|
256
256
|
|
257
257
|
If you are interested how a model scales for a given input use `fit_at`. This method expects that there is a fit model generated using [infer_trend](#22-infer_trend).
|
258
258
|
|
259
|
-
For example, measuring Fibonacci recursive algorithm
|
259
|
+
For example, measuring Fibonacci recursive algorithm:
|
260
260
|
|
261
261
|
```ruby
|
262
|
+
numbers = Benchmark::Trend.range(1, 28, ratio: 2)
|
263
|
+
trend, trends = Benchmark::Trend.infer_trend(numbers) do |n, i|
|
264
|
+
fibonacci(n)
|
265
|
+
end
|
266
|
+
```
|
267
|
+
|
268
|
+
We get the following results:
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
trends[trend]
|
262
272
|
# =>
|
263
273
|
# {:trend=>"1.38 * 0.00^x",
|
264
274
|
# :slope=>1.382889711685203,
|
@@ -269,7 +279,7 @@ For example, measuring Fibonacci recursive algorithm we have the following resul
|
|
269
279
|
And checking model at input of `50`:
|
270
280
|
|
271
281
|
```ruby
|
272
|
-
Benchamrk::Trend.fit_at(:
|
282
|
+
Benchamrk::Trend.fit_at(trend, n: 50, slope: trends[trend][:slope], intercept: trends[trend][:intercept])
|
273
283
|
# => 41.8558455915123
|
274
284
|
```
|
275
285
|
|
@@ -278,7 +288,7 @@ We can see that Fibonacci with just a number 50 will take around 42 seconds to g
|
|
278
288
|
How about Fibonacci with 100 as an input?
|
279
289
|
|
280
290
|
```ruby
|
281
|
-
Benchamrk::Trend.fit_at(:
|
291
|
+
Benchamrk::Trend.fit_at(trend, n: 100, slope: trends[trend][:slope], intercept: trends[trend][:intercept])
|
282
292
|
# => 458282633.9777338
|
283
293
|
```
|
284
294
|
|
data/lib/benchmark-trend.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "benchmark/trend"
|
data/lib/benchmark/trend.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require_relative 'trend/version'
|
3
|
+
require_relative "trend/clock"
|
4
|
+
require_relative "trend/version"
|
6
5
|
|
7
6
|
module Benchmark
|
8
7
|
module Trend
|
@@ -14,31 +13,6 @@ module Benchmark
|
|
14
13
|
private_class_method(method)
|
15
14
|
end
|
16
15
|
|
17
|
-
if defined?(Process::CLOCK_MONOTONIC)
|
18
|
-
# Object representing current time
|
19
|
-
def time_now
|
20
|
-
Process.clock_gettime Process::CLOCK_MONOTONIC
|
21
|
-
end
|
22
|
-
module_function :time_now
|
23
|
-
else
|
24
|
-
# Object represeting current time
|
25
|
-
def time_now
|
26
|
-
Time.now
|
27
|
-
end
|
28
|
-
module_function :time_now
|
29
|
-
end
|
30
|
-
|
31
|
-
# Measure time elapsed with a monotonic clock
|
32
|
-
#
|
33
|
-
# @public
|
34
|
-
def clock_time
|
35
|
-
before = time_now
|
36
|
-
yield
|
37
|
-
after = time_now
|
38
|
-
after - before
|
39
|
-
end
|
40
|
-
module_function :clock_time
|
41
|
-
|
42
16
|
# Generate a range of inputs spaced by powers.
|
43
17
|
#
|
44
18
|
# The default range is generated in the multiples of 8.
|
@@ -106,7 +80,7 @@ module Benchmark
|
|
106
80
|
measurements = []
|
107
81
|
|
108
82
|
repeat.times do
|
109
|
-
measurements <<
|
83
|
+
measurements << Clock.measure { work.(input, i) }
|
110
84
|
end
|
111
85
|
|
112
86
|
times << measurements.reduce(&:+).to_f / measurements.size
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Benchmark
|
4
|
+
module Trend
|
5
|
+
# Clock that represents monotonic time
|
6
|
+
module Clock
|
7
|
+
# Microseconds per second
|
8
|
+
MICROSECONDS_PER_SECOND = 1_000_000
|
9
|
+
|
10
|
+
# Microseconds per 100ms
|
11
|
+
MICROSECONDS_PER_100MS = 100_000
|
12
|
+
|
13
|
+
class_definition = Class.new do
|
14
|
+
def initialize
|
15
|
+
super()
|
16
|
+
@last_time = Time.now.to_f
|
17
|
+
@lock = Mutex.new
|
18
|
+
end
|
19
|
+
|
20
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
21
|
+
# @api private
|
22
|
+
def now
|
23
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
# @api private
|
27
|
+
def now
|
28
|
+
@lock.synchronize do
|
29
|
+
current = Time.now.to_f
|
30
|
+
if @last_time < current
|
31
|
+
@last_time = current
|
32
|
+
else # clock moved back in time
|
33
|
+
@last_time += 0.000_001
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
MONOTONIC_CLOCK = class_definition.new
|
41
|
+
private_constant :MONOTONIC_CLOCK
|
42
|
+
|
43
|
+
# Current monotonic time
|
44
|
+
#
|
45
|
+
# @return [Float]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def now
|
49
|
+
MONOTONIC_CLOCK.now
|
50
|
+
end
|
51
|
+
module_function :now
|
52
|
+
|
53
|
+
# Measure time elapsed with a monotonic clock
|
54
|
+
#
|
55
|
+
# @return [Float]
|
56
|
+
#
|
57
|
+
# @public
|
58
|
+
def measure
|
59
|
+
before = now
|
60
|
+
yield
|
61
|
+
after = now
|
62
|
+
after - before
|
63
|
+
end
|
64
|
+
module_function :measure
|
65
|
+
end # Clock
|
66
|
+
end # Perf
|
67
|
+
end # Benchmark
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: benchmark-trend
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.5.0
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.5.0
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,36 +43,31 @@ description: Benchmark::Trend will help you estimate the computational complexit
|
|
57
43
|
times, and then fitting these observations into a model that best predicts how a
|
58
44
|
given Ruby code will scale as a function of growing workload.
|
59
45
|
email:
|
60
|
-
-
|
46
|
+
- piotr@piotrmurach.com
|
61
47
|
executables: []
|
62
48
|
extensions: []
|
63
|
-
extra_rdoc_files:
|
49
|
+
extra_rdoc_files:
|
50
|
+
- README.md
|
51
|
+
- CHANGELOG.md
|
52
|
+
- LICENSE.txt
|
64
53
|
files:
|
65
54
|
- CHANGELOG.md
|
66
55
|
- LICENSE.txt
|
67
56
|
- README.md
|
68
|
-
- Rakefile
|
69
|
-
- benchmark-trend.gemspec
|
70
57
|
- lib/benchmark-trend.rb
|
71
58
|
- lib/benchmark/trend.rb
|
59
|
+
- lib/benchmark/trend/clock.rb
|
72
60
|
- lib/benchmark/trend/version.rb
|
73
|
-
- spec/spec_helper.rb
|
74
|
-
- spec/unit/fit_at_spec.rb
|
75
|
-
- spec/unit/fit_exp_spec.rb
|
76
|
-
- spec/unit/fit_linear_spec.rb
|
77
|
-
- spec/unit/fit_log_spec.rb
|
78
|
-
- spec/unit/fit_power_spec.rb
|
79
|
-
- spec/unit/format_fit_spec.rb
|
80
|
-
- spec/unit/infer_trend_spec.rb
|
81
|
-
- spec/unit/measure_execution_time_spec.rb
|
82
|
-
- spec/unit/range_spec.rb
|
83
|
-
- tasks/console.rake
|
84
|
-
- tasks/coverage.rake
|
85
|
-
- tasks/spec.rake
|
86
61
|
homepage: https://github.com/piotrmurach/benchmark-trend
|
87
62
|
licenses:
|
88
63
|
- MIT
|
89
|
-
metadata:
|
64
|
+
metadata:
|
65
|
+
allowed_push_host: https://rubygems.org
|
66
|
+
bug_tracker_uri: https://github.com/piotrmurach/benchmark-trend/issues
|
67
|
+
changelog_uri: https://github.com/piotrmurach/benchmark-trend/CHANGELOG.md
|
68
|
+
documentation_uri: https://www.rubydoc.info/gems/benchmark-trend
|
69
|
+
homepage_uri: https://github.com/piotrmurach/benchmark-trend
|
70
|
+
source_code_uri: https://github.com/piotrmurach/benchmark-trend
|
90
71
|
post_install_message:
|
91
72
|
rdoc_options: []
|
92
73
|
require_paths:
|
@@ -102,8 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
83
|
- !ruby/object:Gem::Version
|
103
84
|
version: '0'
|
104
85
|
requirements: []
|
105
|
-
rubygems_version: 3.
|
86
|
+
rubygems_version: 3.1.2
|
106
87
|
signing_key:
|
107
88
|
specification_version: 4
|
108
|
-
summary: Measure
|
89
|
+
summary: Measure performance trends of Ruby code based on the input size distribution.
|
109
90
|
test_files: []
|
data/Rakefile
DELETED
data/benchmark-trend.gemspec
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
lib = File.expand_path("../lib", __FILE__)
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require 'benchmark/trend/version'
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "benchmark-trend"
|
7
|
-
spec.version = Benchmark::Trend::VERSION
|
8
|
-
spec.authors = ["Piotr Murach"]
|
9
|
-
spec.email = ["me@piotrmurach.com"]
|
10
|
-
|
11
|
-
spec.summary = %q{Measure pefromance trends of Ruby code based on the input size distribution.}
|
12
|
-
spec.description = %q{Benchmark::Trend will help you estimate the computational complexity of Ruby code by running it on inputs increasing in size, measuring their execution times, and then fitting these observations into a model that best predicts how a given Ruby code will scale as a function of growing workload.}
|
13
|
-
spec.homepage = "https://github.com/piotrmurach/benchmark-trend"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = Dir['{lib,spec}/**/*.rb']
|
17
|
-
spec.files += Dir['tasks/*', 'benchmark-trend.gemspec']
|
18
|
-
spec.files += Dir['README.md', 'CHANGELOG.md', 'LICENSE.txt', 'Rakefile']
|
19
|
-
spec.bindir = "exe"
|
20
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
-
spec.require_paths = ["lib"]
|
22
|
-
|
23
|
-
spec.required_ruby_version = '>= 2.0.0'
|
24
|
-
|
25
|
-
spec.add_development_dependency "bundler", ">= 1.5.0"
|
26
|
-
spec.add_development_dependency "rake"
|
27
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
28
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
if ENV['COVERAGE'] || ENV['TRAVIS']
|
4
|
-
require 'simplecov'
|
5
|
-
require 'coveralls'
|
6
|
-
|
7
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
-
SimpleCov::Formatter::HTMLFormatter,
|
9
|
-
Coveralls::SimpleCov::Formatter
|
10
|
-
]
|
11
|
-
|
12
|
-
SimpleCov.start do
|
13
|
-
command_name 'spec'
|
14
|
-
add_filter 'spec'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
require "bundler/setup"
|
19
|
-
require "benchmark/trend"
|
20
|
-
|
21
|
-
RSpec.configure do |config|
|
22
|
-
# Enable flags like --only-failures and --next-failure
|
23
|
-
config.example_status_persistence_file_path = ".rspec_status"
|
24
|
-
|
25
|
-
# Disable RSpec exposing methods globally on `Module` and `main`
|
26
|
-
config.disable_monkey_patching!
|
27
|
-
|
28
|
-
config.expect_with :rspec do |c|
|
29
|
-
c.syntax = :expect
|
30
|
-
end
|
31
|
-
end
|
data/spec/unit/fit_at_spec.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#fit_at' do
|
4
|
-
it "evalutes logarithmic fit model at a given value" do
|
5
|
-
val = Benchmark::Trend.fit_at(:logarithmic, slope: 1.5, intercept: 2, n: 10)
|
6
|
-
|
7
|
-
expect(val).to be_within(0.1).of(5.45)
|
8
|
-
end
|
9
|
-
|
10
|
-
it "evalutes linear fit model at a given value" do
|
11
|
-
val = Benchmark::Trend.fit_at(:linear, slope: 1.5, intercept: 2, n: 10)
|
12
|
-
|
13
|
-
expect(val).to eq(17)
|
14
|
-
end
|
15
|
-
|
16
|
-
it "evalutes power fit model at a given value" do
|
17
|
-
val = Benchmark::Trend.fit_at(:power, slope: 1.5, intercept: 2, n: 10)
|
18
|
-
|
19
|
-
expect(val).to be_within(0.1).of(63.24)
|
20
|
-
end
|
21
|
-
|
22
|
-
it "evalutes power fit model at a given value" do
|
23
|
-
val = Benchmark::Trend.fit_at(:exponential, slope: 1.5, intercept: 2, n: 10)
|
24
|
-
|
25
|
-
expect(val).to be_within(0.1).of(115.33)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "doesn't recognise fit model" do
|
29
|
-
expect {
|
30
|
-
Benchmark::Trend.fit_at(:unknown, slope: 1.5, intercept: 2, n: 10)
|
31
|
-
}.to raise_error(ArgumentError, "Unknown fit type: unknown")
|
32
|
-
end
|
33
|
-
|
34
|
-
it "doesn't allow incorrect input size" do
|
35
|
-
expect {
|
36
|
-
Benchmark::Trend.fit_at(:linear, slope: 1.5, intercept: 2, n: -1)
|
37
|
-
}.to raise_error(ArgumentError, "Incorrect input size: -1")
|
38
|
-
end
|
39
|
-
end
|
data/spec/unit/fit_exp_spec.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#fit_exp' do
|
4
|
-
it "calculates a perfect exponential fit" do
|
5
|
-
xs = [1, 2, 3, 4, 5]
|
6
|
-
ys = xs.map { |x| 1.5 * (2 ** x) }
|
7
|
-
|
8
|
-
a, b, rr = Benchmark::Trend.fit_exp(xs, ys)
|
9
|
-
|
10
|
-
expect(a).to be_within(0.001).of(2.0)
|
11
|
-
expect(b).to be_within(0.001).of(1.5)
|
12
|
-
expect(rr).to be_within(0.001).of(0.999)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "calculates best exponential fit of y = 1.30*(1.46)^x" do
|
16
|
-
# the number y (in millions) of mobiles subscriberes from 1988 to 1997 USA
|
17
|
-
xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
18
|
-
ys = [1.6, 2.7, 4.4, 6.4, 8.9, 13.1, 19.3, 28.2, 38.2, 48.7]
|
19
|
-
|
20
|
-
a, b, rr = Benchmark::Trend.fit_exp(xs, ys)
|
21
|
-
|
22
|
-
expect(a).to be_within(0.001).of(1.458)
|
23
|
-
expect(b).to be_within(0.001).of(1.300)
|
24
|
-
expect(rr).to be_within(0.001).of(0.993)
|
25
|
-
end
|
26
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#fit_linear' do
|
4
|
-
it "calculates perfect linear fit" do
|
5
|
-
xs = [1, 2, 3, 4, 5]
|
6
|
-
ys = xs.map { |x| 3.0 * x + 1.0 }
|
7
|
-
|
8
|
-
a, b, rr = Benchmark::Trend.fit_linear(xs, ys)
|
9
|
-
|
10
|
-
expect(a).to eq(3.0)
|
11
|
-
expect(b).to eq(1.0)
|
12
|
-
expect(rr).to be_within(0.1).of(1)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "calculates linear fit with noise" do
|
16
|
-
xs = [3.4, 3.8, 4.1, 2.2, 2.6, 2.9, 2.0, 2.7, 1.9, 3.4]
|
17
|
-
ys = [5.5, 5.9, 6.5, 3.3, 3.6, 4.6, 2.9, 3.6, 3.1, 4.9]
|
18
|
-
|
19
|
-
a, b, rr = Benchmark::Trend.fit_linear(xs, ys)
|
20
|
-
|
21
|
-
expect(a).to be_within(0.1).of(1.64)
|
22
|
-
expect(b).to be_within(0.1).of(-0.36)
|
23
|
-
expect(rr).to be_within(0.001).of(0.953)
|
24
|
-
end
|
25
|
-
|
26
|
-
it "calculates perfect constant fit" do
|
27
|
-
xs = [1, 2, 3, 4, 5]
|
28
|
-
ys = [6.0, 6.0, 6.0, 6.0, 6.0]
|
29
|
-
|
30
|
-
a, b, rr = Benchmark::Trend.fit_linear(xs, ys)
|
31
|
-
|
32
|
-
expect(a).to eq(0)
|
33
|
-
expect(b).to eq(6)
|
34
|
-
expect(rr).to eq(1)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "calculates constant fit with noise" do
|
38
|
-
xs = [1, 2, 3, 4, 5]
|
39
|
-
ys = [1.0, 0.9, 1.0, 1.1, 1.0]
|
40
|
-
|
41
|
-
a, b, rr = Benchmark::Trend.fit_linear(xs, ys)
|
42
|
-
|
43
|
-
expect(a).to eq(0.02)
|
44
|
-
expect(b).to be_within(0.01).of(0.94)
|
45
|
-
expect(rr).to be_within(0.01).of(0.19)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "raises when no variation in data" do
|
49
|
-
xs = [1, 1, 1, 1, 1]
|
50
|
-
ys = [1.0, 0.9, 1.0, 1.1, 1.0]
|
51
|
-
|
52
|
-
expect {
|
53
|
-
Benchmark::Trend.fit_linear(xs, ys)
|
54
|
-
}.to raise_error(ArgumentError, "No variation in data [1, 1, 1, 1, 1]")
|
55
|
-
end
|
56
|
-
end
|
data/spec/unit/fit_log_spec.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#fit_log' do
|
4
|
-
it "calculates perfect logarithmic fit" do
|
5
|
-
xs = [1, 2, 3, 4, 5]
|
6
|
-
ys = xs.map { |x| 1.5 * Math.log(x) + 1.0 }
|
7
|
-
|
8
|
-
a, b, rr = Benchmark::Trend.fit_log(xs, ys)
|
9
|
-
|
10
|
-
expect(a).to be_within(0.001).of(1.5)
|
11
|
-
expect(b).to be_within(0.001).of(1.0)
|
12
|
-
expect(rr).to be_within(0.001).of(1)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "calculates logarithmic fit with noise" do
|
16
|
-
# life expectancy in USA data from 1900 in 10 years periods
|
17
|
-
xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
18
|
-
ys = [47.3, 50.0, 54.1, 59.7, 62.9, 68.2, 69.7, 70.8, 73.7, 75.4, 76.8, 78.7]
|
19
|
-
|
20
|
-
a, b, rr = Benchmark::Trend.fit_log(xs, ys)
|
21
|
-
|
22
|
-
expect(a).to be_within(0.001).of(13.857)
|
23
|
-
expect(b).to be_within(0.001).of(42.527)
|
24
|
-
expect(rr).to be_within(0.001).of(0.956)
|
25
|
-
end
|
26
|
-
end
|
data/spec/unit/fit_power_spec.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#fit_power' do
|
4
|
-
it 'calculates perfect power fit' do
|
5
|
-
xs = [1, 2, 3, 4, 5]
|
6
|
-
ys = xs.map { |x| 1.5 * (x ** 2) }
|
7
|
-
|
8
|
-
a, b, rr = Benchmark::Trend.fit_power(xs, ys)
|
9
|
-
|
10
|
-
expect(a).to be_within(0.001).of(2.0)
|
11
|
-
expect(b).to be_within(0.001).of(1.5)
|
12
|
-
expect(rr).to be_within(0.001).of(1.0)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "calcualtes best power fit of y = x^1.5" do
|
16
|
-
# Mercury Venus Earth Mars Jupiter Saturn
|
17
|
-
xs = [0.387, 0.723, 1.00, 1.524, 5.203, 9.539] # distance from the sun
|
18
|
-
ys = [0.241, 0.615, 1.00, 1.881, 11.862, 29.458] # period in Earth's years
|
19
|
-
|
20
|
-
a, b, rr = Benchmark::Trend.fit_power(xs, ys)
|
21
|
-
|
22
|
-
expect(a).to be_within(0.001).of(1.5)
|
23
|
-
expect(b).to be_within(0.001).of(1.0)
|
24
|
-
expect(rr).to be_within(0.001).of(0.999)
|
25
|
-
end
|
26
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#format_fit' do
|
4
|
-
it "returns a logarithmic template" do
|
5
|
-
format = Benchmark::Trend.format_fit(:logarithmic)
|
6
|
-
expect(format).to eq("%.2f + %.2f*ln(x)")
|
7
|
-
end
|
8
|
-
|
9
|
-
it "returns a linear template" do
|
10
|
-
format = Benchmark::Trend.format_fit(:linear)
|
11
|
-
expect(format).to eq("%.2f + %.2f*x")
|
12
|
-
end
|
13
|
-
|
14
|
-
it "returns a power template" do
|
15
|
-
format = Benchmark::Trend.format_fit(:power)
|
16
|
-
expect(format).to eq("%.2f * x^%.2f")
|
17
|
-
end
|
18
|
-
|
19
|
-
it "returns a exponential template" do
|
20
|
-
format = Benchmark::Trend.format_fit(:exponential)
|
21
|
-
expect(format).to eq("%.2f * %.2f^x")
|
22
|
-
end
|
23
|
-
|
24
|
-
it "fails to recognise fit type" do
|
25
|
-
expect {
|
26
|
-
Benchmark::Trend.format_fit(:unknown)
|
27
|
-
}.to raise_error(ArgumentError, "Unknown type: 'unknown'")
|
28
|
-
end
|
29
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#infer_trend' do
|
4
|
-
# exponential
|
5
|
-
def fibonacci(n)
|
6
|
-
n == 1 || n == 0 ? n : fibonacci(n - 1) + fibonacci(n - 2)
|
7
|
-
end
|
8
|
-
|
9
|
-
# linear
|
10
|
-
def fib_mem(n, acc = {"0" => 0, "1" => 1})
|
11
|
-
return n if n < 2
|
12
|
-
|
13
|
-
if !acc.key?(n.to_s)
|
14
|
-
acc[n.to_s] = fib_mem(n - 1, acc) + fib_mem(n - 2, acc)
|
15
|
-
end
|
16
|
-
acc[n.to_s]
|
17
|
-
end
|
18
|
-
|
19
|
-
# linear
|
20
|
-
def fib_iter(n)
|
21
|
-
a, b = 0, 1
|
22
|
-
n.times { a, b = b, a + b}
|
23
|
-
a
|
24
|
-
end
|
25
|
-
|
26
|
-
# logarithmic
|
27
|
-
def fib_const(n)
|
28
|
-
phi = (1 + Math.sqrt(5))/2
|
29
|
-
(phi ** n / Math.sqrt(5)).round
|
30
|
-
end
|
31
|
-
|
32
|
-
it "infers constant trend" do
|
33
|
-
numbers = Benchmark::Trend.range(1, 100_000)
|
34
|
-
trend, = Benchmark::Trend.infer_trend(numbers, repeat: 100) do |n|
|
35
|
-
n
|
36
|
-
end
|
37
|
-
|
38
|
-
expect(trend).to eq(:constant)
|
39
|
-
end
|
40
|
-
|
41
|
-
it "infers fibonacci classic algorithm trend to be exponential" do
|
42
|
-
trend, trends = Benchmark::Trend.infer_trend((1..20), repeat: 10) do |n|
|
43
|
-
fibonacci(n)
|
44
|
-
end
|
45
|
-
|
46
|
-
expect(trend).to eq(:exponential)
|
47
|
-
expect(trends).to match(
|
48
|
-
hash_including(:exponential, :power, :linear, :logarithmic))
|
49
|
-
expect(trends[:exponential]).to match(
|
50
|
-
hash_including(:trend, :slope, :intercept, :residual)
|
51
|
-
)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "infers fibonacci iterative algorithm trend to be linear" do
|
55
|
-
numbers = Benchmark::Trend.range(1, 20_000)
|
56
|
-
trend, _ = Benchmark::Trend.infer_trend(numbers) do |n|
|
57
|
-
fib_iter(n)
|
58
|
-
end
|
59
|
-
|
60
|
-
expect(trend).to eq(:linear)
|
61
|
-
end
|
62
|
-
|
63
|
-
it "infers fibonacci constant algorithm trend to be constant" do
|
64
|
-
# exponetiation by squaring has logarithmic complexity
|
65
|
-
numbers = Benchmark::Trend.range(1, 1400, ratio: 2)
|
66
|
-
trend, trends = Benchmark::Trend.infer_trend(numbers, repeat: 100) do |n|
|
67
|
-
fib_const(n)
|
68
|
-
end
|
69
|
-
|
70
|
-
expect(trend).to eq(:constant)
|
71
|
-
expect(trends[trend][:slope]).to be_within(0.0001).of(0)
|
72
|
-
end
|
73
|
-
|
74
|
-
it "infers finding maximum value trend to be linear" do
|
75
|
-
array_sizes = Benchmark::Trend.range(1, 100_000)
|
76
|
-
numbers = array_sizes.map { |n| Array.new(n) { rand(n) } }
|
77
|
-
|
78
|
-
trend, trends = Benchmark::Trend.infer_trend(array_sizes, repeat: 10) do |n, i|
|
79
|
-
numbers[i].max
|
80
|
-
end
|
81
|
-
|
82
|
-
expect(trend).to eq(:linear)
|
83
|
-
expect(trends[trend][:slope]).to be_within(0.0001).of(0)
|
84
|
-
end
|
85
|
-
|
86
|
-
it "infers binary search trend to be constant" do
|
87
|
-
range = Benchmark::Trend.range(10, 8 << 10, ratio: 2)
|
88
|
-
numbers = range.reduce([]) { |acc, n| acc << (1..n).to_a; acc }
|
89
|
-
|
90
|
-
trend, trends = Benchmark::Trend.infer_trend(range, repeat: 100) do |n, i|
|
91
|
-
numbers[i].bsearch { |x| x == n/2 }
|
92
|
-
end
|
93
|
-
|
94
|
-
expect(trend).to eq(:constant)
|
95
|
-
expect(trends[trend][:slope]).to be_within(0.0001).of(0)
|
96
|
-
end
|
97
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
#
|
3
|
-
RSpec.describe Benchmark::Trend, "#measure_execution_time" do
|
4
|
-
it "measures performance times" do
|
5
|
-
func = -> (x, i) { x ** 2 }
|
6
|
-
|
7
|
-
data = Benchmark::Trend.measure_execution_time(&func)
|
8
|
-
|
9
|
-
expect(data[0]).to eq([1, 8, 64, 512, 4096, 10000])
|
10
|
-
expect(data[1]).to match([
|
11
|
-
be_within(0.001).of(0.00001),
|
12
|
-
be_within(0.001).of(0.00001),
|
13
|
-
be_within(0.001).of(0.00001),
|
14
|
-
be_within(0.001).of(0.00001),
|
15
|
-
be_within(0.001).of(0.00001),
|
16
|
-
be_within(0.001).of(0.00001)
|
17
|
-
])
|
18
|
-
end
|
19
|
-
end
|
data/spec/unit/range_spec.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Benchmark::Trend, '#range' do
|
4
|
-
it "creates default range" do
|
5
|
-
range = Benchmark::Trend.range(8, 8 << 10)
|
6
|
-
expect(range).to eq([8, 64, 512, 4096, 8192])
|
7
|
-
end
|
8
|
-
|
9
|
-
it "creates range with 2 multiplier" do
|
10
|
-
range = Benchmark::Trend.range(8, 8 << 10, ratio: 2)
|
11
|
-
expect(range).to eq([8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192])
|
12
|
-
end
|
13
|
-
|
14
|
-
it "checks range start to be valid" do
|
15
|
-
expect {
|
16
|
-
Benchmark::Trend.range(-2, 10_000)
|
17
|
-
}.to raise_error(ArgumentError, "Range value: -2 needs to be greater than 0")
|
18
|
-
end
|
19
|
-
|
20
|
-
it "checks range end to be greater than start" do
|
21
|
-
expect {
|
22
|
-
Benchmark::Trend.range(8, 2)
|
23
|
-
}.to raise_error(ArgumentError, "Range value: 2 needs to be greater than 8")
|
24
|
-
end
|
25
|
-
|
26
|
-
it "checks multiplier to be valid" do
|
27
|
-
expect {
|
28
|
-
Benchmark::Trend.range(8, 32, ratio: 1)
|
29
|
-
}.to raise_error(ArgumentError, "Range value: 1 needs to be greater than 2")
|
30
|
-
end
|
31
|
-
end
|
data/tasks/console.rake
DELETED
data/tasks/coverage.rake
DELETED
data/tasks/spec.rake
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
|
6
|
-
desc 'Run all specs'
|
7
|
-
RSpec::Core::RakeTask.new(:spec) do |task|
|
8
|
-
task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
|
9
|
-
end
|
10
|
-
|
11
|
-
namespace :spec do
|
12
|
-
desc 'Run unit specs'
|
13
|
-
RSpec::Core::RakeTask.new(:unit) do |task|
|
14
|
-
task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
|
15
|
-
end
|
16
|
-
|
17
|
-
desc 'Run integration specs'
|
18
|
-
RSpec::Core::RakeTask.new(:integration) do |task|
|
19
|
-
task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
rescue LoadError
|
24
|
-
%w[spec spec:unit spec:integration].each do |name|
|
25
|
-
task name do
|
26
|
-
$stderr.puts "In order to run #{name}, do `gem install rspec`"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|