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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3e27367eb5b2bd4fdb6f42ae8d142eafb36d20b79391ba1cc316f5e3d816f37
4
- data.tar.gz: 5e894156903f8b74f3ce543d1ee01c44567fb1f681b926b8ccfcdc28929773c2
3
+ metadata.gz: 6729682c2148955fb9411b540070d10a51d3dd2d0ab6907fcedc909c250da34f
4
+ data.tar.gz: 391408701a394ddf0d61561ddc27d367a82b77a46c61e2d244ec4f1f8f444bd0
5
5
  SHA512:
6
- metadata.gz: cb7f6c061e9fafd944f1ec0abc7112ad551a4065875d8496c3d9463552435263ca443c12be24eb457f7be0340dd0d4d16cd67f42be6dc030b2276b52e2890f85
7
- data.tar.gz: 3f077ee1c12866e94f3e8b75cb678830386ead23aa6002db3033701862b9da123841855f29dedbe16c918fb7efcd01f9872551996b8b5b07c2f7a40af69e579f
6
+ metadata.gz: 14612b7218f12783d8b7cb935a04cd1e9146361faa4a0004b4b221c02e0215c63dcdfb235298a49375f39096b95a15efcf8503660452469ad1bde9cf9f455eb2
7
+ data.tar.gz: dc5a88b5536c9879c2f893e9710980926aae76d8af9c4ae47322727753eac59df9bb4614be6f1d7761f0cbb5b3d4141b0eb89862cbea823bc67248a35fd5147a
@@ -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 we have the following results:
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(:exponential, slope: 1.382889711685203, intercept: 3.822775903539121e-06, n: 50)
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(:exponential, slope: 1.382889711685203, intercept: 3.822775903539121e-06, n: 100)
291
+ Benchamrk::Trend.fit_at(trend, n: 100, slope: trends[trend][:slope], intercept: trends[trend][:intercept])
282
292
  # => 458282633.9777338
283
293
  ```
284
294
 
@@ -1 +1 @@
1
- require_relative 'benchmark/trend'
1
+ require_relative "benchmark/trend"
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'benchmark'
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 << clock_time { work.(input, i) }
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Benchmark
4
4
  module Trend
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end # Trend
7
7
  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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-21 00:00:00.000000000 Z
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
- - me@piotrmurach.com
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.0.3
86
+ rubygems_version: 3.1.2
106
87
  signing_key:
107
88
  specification_version: 4
108
- summary: Measure pefromance trends of Ruby code based on the input size distribution.
89
+ summary: Measure performance trends of Ruby code based on the input size distribution.
109
90
  test_files: []
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- FileList['tasks/**/*.rake'].each(&method(:import))
4
-
5
- desc 'Run all specs'
6
- task ci: %w[ spec ]
7
-
8
- task default: :spec
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,11 +0,0 @@
1
- # encoding: utf-8
2
-
3
- desc 'Load gem inside irb console'
4
- task :console do
5
- require 'irb'
6
- require 'irb/completion'
7
- require File.join(__FILE__, '../../lib/benchmark-trend')
8
- ARGV.clear
9
- IRB.start
10
- end
11
- task c: %w[ console ]
@@ -1,11 +0,0 @@
1
- # encoding: utf-8
2
-
3
- desc 'Measure code coverage'
4
- task :coverage do
5
- begin
6
- original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true'
7
- Rake::Task['spec'].invoke
8
- ensure
9
- ENV['COVERAGE'] = original
10
- end
11
- end
@@ -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