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 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