benchmark-trend 0.3.0 → 0.4.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 +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
|