benchmark-perf 0.4.0 → 0.5.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: b9e96517aac61f030785b74eebd97e06e49e74eda0bb7ce37854ed454817a5f9
4
- data.tar.gz: 82a06c6df41203a33abbe439fe30fc0df64a2a22c26e5ef730fcb065733e0dbd
3
+ metadata.gz: 23be9b198b35a0cd2ba9fce98fe449cc11bf603d85361fd296fce4d25c4e6fcb
4
+ data.tar.gz: 4e88b12e133ad01323eca2dbae4aa1a36cfcc44390ef9883785fa9df14009618
5
5
  SHA512:
6
- metadata.gz: b09c694ef8fe8d30f21407a0a16d3ce8a86ac139257a08876ad1cf2cce298d2dfccee5f2078e75ec2420985bcd8588383a442a9f1e1d85f52a17ea4889b50bb2
7
- data.tar.gz: 4c4e308e46b4874bf656f15716500149a1d3daa869f330714c478bac1d50cb3b224cb6f2587a385d1adcc1545b45dd003020696a8468dec645d68e6531175c99
6
+ metadata.gz: 8cc2086bbfcedfa67021a9cd6fdc9ea0a6b04bbedf1457cd8a1921d28dde118a328b95568a0d361313ae0c4f16f18187fc023f76e6f82b185cb28355859d941b
7
+ data.tar.gz: '0962f8911d08ec0be76097fc7bb7a225031ed943214c6be85dfebd909e606168463061013bd22cfc2e18c782861f6e3b5f12c51f935eec300e5923b63a8e0016'
@@ -1,5 +1,14 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.5.0] - 2019-04-21
4
+
5
+ ### Added
6
+ * Add :subprocess option To ExecutionTime#run for toggling forking behaviour
7
+
8
+ ### Changed
9
+ * Remove ExecutiomTime#linear_range
10
+ * Change to relax development dependencies versions
11
+
3
12
  ## [v0.4.0] - 2018-09-30
4
13
 
5
14
  ### Changed
data/README.md CHANGED
@@ -77,6 +77,14 @@ If you're interested in having debug output to see exact measurements for each i
77
77
  Benchmark::Perf::ExecutionTime.run(io: $stdout) { ... }
78
78
  ```
79
79
 
80
+ By default all measurements are done in subprocess to isolate them from other process activities. This may have negative consequences, for example when your code uses database connections and transactions. To switch this behaviour off use `:subprocess` option.
81
+
82
+ ```ruby
83
+ Benchmark::Perf::ExeuctionTime.run(subprocess: false) { ... }
84
+ ```
85
+
86
+ Or use the environment variable `RUN_IN_SUBPROCESS` to toggle the behaviour.
87
+
80
88
  ### 2.2 Iterations
81
89
 
82
90
  In order to check how many iterations per second a given code takes do:
@@ -105,6 +113,10 @@ Benchmark::Perf::Iteration.run(time: 3.5) { ... } # 3.5 seconds
105
113
  4. Push to the branch (`git push origin my-new-feature`)
106
114
  5. Create a new Pull Request
107
115
 
116
+ ## Code of Conduct
117
+
118
+ Everyone interacting in the Strings project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/benchmark-perf/blob/master/CODE_OF_CONDUCT.md).
119
+
108
120
  ## Copyright
109
121
 
110
- Copyright (c) 2016-2018 Piotr Murach. See LICENSE for further details.
122
+ Copyright (c) 2016 Piotr Murach. See LICENSE for further details.
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require "bundler/gem_tasks"
4
2
 
5
3
  FileList['tasks/**/*.rake'].each(&method(:import))
@@ -8,4 +6,3 @@ desc 'Run all specs'
8
6
  task ci: %w[ spec ]
9
7
 
10
8
  task default: :spec
11
-
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.name = "benchmark-perf"
7
7
  spec.version = Benchmark::Perf::VERSION
8
8
  spec.authors = ["Piotr Murach"]
9
- spec.email = [""]
9
+ spec.email = ["me@piotrmurach.com"]
10
10
  spec.summary = %q{Execution time and iteration performance benchmarking}
11
11
  spec.description = %q{Execution time and iteration performance benchmarking}
12
12
  spec.homepage = ""
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.required_ruby_version = '>= 2.0.0'
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1.16'
24
+ spec.add_development_dependency 'bundler', '>= 1.16'
25
25
  spec.add_development_dependency 'rspec', '~> 3.0'
26
- spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rake'
27
27
  end
@@ -18,6 +18,7 @@ module Benchmark
18
18
  # @api public
19
19
  def average(measurements)
20
20
  return 0 if measurements.empty?
21
+
21
22
  measurements.reduce(&:+).to_f / measurements.size
22
23
  end
23
24
  module_function :average
@@ -31,6 +32,7 @@ module Benchmark
31
32
  # @api public
32
33
  def variance(measurements)
33
34
  return 0 if measurements.empty?
35
+
34
36
  avg = average(measurements)
35
37
  total = measurements.reduce(0) do |sum, x|
36
38
  sum + (x - avg)**2
@@ -46,6 +48,7 @@ module Benchmark
46
48
  # @api public
47
49
  def std_dev(measurements)
48
50
  return 0 if measurements.empty?
51
+
49
52
  Math.sqrt(variance(measurements))
50
53
  end
51
54
  module_function :std_dev
@@ -81,14 +84,13 @@ module Benchmark
81
84
  def time_now
82
85
  Process.clock_gettime Process::CLOCK_MONOTONIC
83
86
  end
84
- module_function :time_now
85
87
  else
86
88
  # Object represeting current time
87
89
  def time_now
88
90
  Time.now
89
91
  end
90
- module_function :time_now
91
92
  end
93
+ module_function :time_now
92
94
 
93
95
  # Measure time elapsed with a monotonic clock
94
96
  #
@@ -6,13 +6,13 @@ module Benchmark
6
6
  #
7
7
  # @api public
8
8
  module ExecutionTime
9
- # Set of ranges in linear progression
9
+ # Check if measurements need to run in subprocess
10
10
  #
11
- # @api public
12
- def linear_range(min, max, step = 1)
13
- (min..max).step(step).to_a
11
+ # @api private
12
+ def run_in_subprocess?
13
+ ENV["RUN_IN_SUBPROCESS"] != 'false' && Process.respond_to?(:fork)
14
14
  end
15
- module_function :linear_range
15
+ module_function :run_in_subprocess?
16
16
 
17
17
  # Isolate run in subprocess
18
18
  #
@@ -23,10 +23,14 @@ module Benchmark
23
23
  # the elapsed time of the measurement
24
24
  #
25
25
  # @api private
26
- def run_in_subprocess(io: nil)
27
- return yield unless Process.respond_to?(:fork)
26
+ def run_in_subprocess(subprocess: true, io: nil)
27
+ return yield unless subprocess && Process.respond_to?(:fork)
28
+ return yield unless run_in_subprocess?
28
29
 
29
30
  reader, writer = IO.pipe
31
+ reader.binmode
32
+ writer.binmode
33
+
30
34
  pid = Process.fork do
31
35
  GC.start
32
36
  GC.disable if ENV['BENCH_DISABLE_GC']
@@ -34,20 +38,21 @@ module Benchmark
34
38
  begin
35
39
  reader.close
36
40
  time = yield
37
-
38
41
  io.print "%9.6f" % data if io
39
42
  Marshal.dump(time, writer)
40
43
  rescue => error
41
44
  Marshal.dump(error, writer)
42
45
  ensure
43
46
  GC.enable if ENV['BENCH_DISABLE_GC']
44
- exit!(0) # run without hooks
47
+ writer.close
48
+ exit # allow finalizers to run
45
49
  end
46
50
  end
47
51
 
48
52
  writer.close unless writer.closed?
49
53
  Process.waitpid(pid)
50
54
  data = Marshal.load(reader)
55
+ reader.close
51
56
  raise data if data.is_a?(Exception)
52
57
  data
53
58
  end
@@ -59,10 +64,11 @@ module Benchmark
59
64
  # the warmup time
60
65
  #
61
66
  # @api private
62
- def run_warmup(warmup: 1, &work)
67
+ def run_warmup(warmup: 1, io: nil, subprocess: true, &work)
63
68
  GC.start
69
+
64
70
  warmup.times do
65
- run_in_subprocess do
71
+ run_in_subprocess(io: io, subprocess: subprocess) do
66
72
  Perf.clock_time(&work)
67
73
  end
68
74
  end
@@ -81,14 +87,15 @@ module Benchmark
81
87
  # average and standard deviation
82
88
  #
83
89
  # @api public
84
- def run(repeat: 1, io: nil, warmup: 1, &work)
90
+ def run(repeat: 1, io: nil, warmup: 1, subprocess: true, &work)
85
91
  check_greater(repeat, 0)
86
92
  measurements = []
87
- run_warmup(warmup: warmup, &work)
93
+
94
+ run_warmup(warmup: warmup, io: io, subprocess: subprocess, &work)
88
95
 
89
96
  repeat.times do
90
97
  GC.start
91
- measurements << run_in_subprocess(io: io) do
98
+ measurements << run_in_subprocess(io: io, subprocess: subprocess) do
92
99
  Perf.clock_time(&work)
93
100
  end
94
101
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Benchmark
4
4
  module Perf
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end # Perf
7
7
  end # Benchmark
@@ -1,6 +1,6 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- if RUBY_VERSION > '1.9' and (ENV['COVERAGE'] || ENV['TRAVIS'])
3
+ if ENV['COVERAGE'] || ENV['TRAVIS']
4
4
  require 'simplecov'
5
5
  require 'coveralls'
6
6
 
@@ -15,7 +15,7 @@ if RUBY_VERSION > '1.9' and (ENV['COVERAGE'] || ENV['TRAVIS'])
15
15
  end
16
16
  end
17
17
 
18
- require 'benchmark-perf'
18
+ require "benchmark-perf"
19
19
 
20
20
  RSpec.configure do |config|
21
21
  config.expect_with :rspec do |expectations|
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Benchmark::Perf, 'arithmetic' do
4
4
  context '#average' do
@@ -1,4 +1,4 @@
1
- # frozen_string_literal
1
+ # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Benchmark::Perf, 'assertions' do
4
4
  it "passes asertion by performing under threshold" do
@@ -1,37 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Benchmark::Perf::ExecutionTime do
4
- it "provides linear range" do
5
- expect(described_class.linear_range(0, 3)).to eq([0,1,2,3])
6
- end
7
-
8
4
  it "provides default benchmark range" do
9
5
  allow(described_class).to receive(:run_in_subprocess).and_return(0.1)
6
+
10
7
  described_class.run(warmup: 0) { 'x' * 1024 }
8
+
11
9
  expect(described_class).to have_received(:run_in_subprocess).once
12
10
  end
13
11
 
14
12
  it "accepts custom number of samples" do
15
13
  allow(described_class).to receive(:run_in_subprocess).and_return(0.1)
14
+
16
15
  described_class.run(repeat: 12, warmup: 0) { 'x' * 1024 }
16
+
17
17
  expect(described_class).to have_received(:run_in_subprocess).exactly(12).times
18
18
  end
19
19
 
20
- it "doesn't accept range smaller than 1" do
20
+ it "runs warmup cycles" do
21
21
  allow(described_class).to receive(:run_in_subprocess).and_return(0.1)
22
+
22
23
  described_class.run(repeat: 1, warmup: 1) { 'x' }
24
+
23
25
  expect(described_class).to have_received(:run_in_subprocess).twice
24
26
  end
25
27
 
28
+ it "doesn't run in subproces when option :run_in_subprocess is set to false",
29
+ if: ::Process.respond_to?(:fork) do
30
+
31
+ allow(::Process).to receive(:fork)
32
+
33
+ described_class.run(subprocess: false) { 'x' * 1024 }
34
+
35
+ expect(::Process).to_not have_received(:fork)
36
+ end
37
+
38
+ it "doesn't run in subprocess when RUN_IN_SUBPROCESS env var is set to false",
39
+ if: ::Process.respond_to?(:fork) do
40
+
41
+ allow(::Process).to receive(:fork)
42
+ allow(ENV).to receive(:[]).with("RUN_IN_SUBPROCESS").and_return('false')
43
+
44
+ described_class.run { 'x' * 1024 }
45
+
46
+ expect(::Process).to_not have_received(:fork)
47
+ end
48
+
26
49
  it "doesn't accept range smaller than 1" do
27
50
  expect {
28
51
  described_class.run(repeat: 0) { 'x' }
29
- }.to raise_error(ArgumentError,
30
- 'Repeat value: 0 needs to be greater than 0')
52
+ }.to raise_error(ArgumentError, 'Repeat value: 0 needs to be greater than 0')
31
53
  end
32
54
 
33
55
  it "provides measurements for 30 samples by default" do
34
56
  sample = described_class.run { 'x' * 1024 }
57
+
35
58
  expect(sample).to all(be < 0.01)
36
59
  end
37
60
 
@@ -43,16 +66,19 @@ RSpec.describe Benchmark::Perf::ExecutionTime do
43
66
 
44
67
  it "measures complex object" do
45
68
  sample = described_class.run { {foo: Object.new, bar: :piotr} }
69
+
46
70
  expect(sample).to all(be < 0.01)
47
71
  end
48
72
 
49
73
  it "executes code to warmup ruby vm" do
50
74
  sample = described_class.run_warmup { 'x' * 1_000_000 }
75
+
51
76
  expect(sample).to eq(1)
52
77
  end
53
78
 
54
79
  it "measures work performance for 10 samples" do
55
80
  sample = described_class.run(repeat: 10) { 'x' * 1_000_000 }
81
+
56
82
  expect(sample.size).to eq(2)
57
83
  expect(sample).to all(be < 0.01)
58
84
  end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  desc 'Load gem inside irb console'
4
4
  task :console do
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  desc 'Measure code coverage'
4
4
  task :coverage do
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  begin
4
4
  require 'rspec/core/rake_task'
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: benchmark-perf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-30 00:00:00.000000000 Z
11
+ date: 2019-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.16'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.16'
27
27
  - !ruby/object:Gem::Dependency
@@ -42,19 +42,19 @@ dependencies:
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '0'
55
55
  description: Execution time and iteration performance benchmarking
56
56
  email:
57
- - ''
57
+ - me@piotrmurach.com
58
58
  executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
@@ -96,8 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  requirements: []
99
- rubyforge_project:
100
- rubygems_version: 2.7.3
99
+ rubygems_version: 3.0.3
101
100
  signing_key:
102
101
  specification_version: 4
103
102
  summary: Execution time and iteration performance benchmarking