rubyperf 1.0.1 → 1.1.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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.1.0
data/lib/perf/meter.rb CHANGED
@@ -22,19 +22,48 @@ module Perf
22
22
  attr_accessor :measurements
23
23
  attr_accessor :current_path
24
24
 
25
- def initialize
25
+ @@overhead = nil
26
+
27
+ # Overhead calculation tuning constants
28
+ OVERHEAD_CALC_MAX_REPETITIONS = 1000
29
+ OVERHEAD_CALC_RUNS = 100
30
+ OVERHEAD_CALC_MIN_TIME = 0.1
31
+
32
+ # Creation of a Perf::Meter allows you to specify if you want to consider the overhead in the calculation or not.
33
+ # The overhead is calculated once and stored away. That way it is always the same in a single run.
34
+
35
+ def initialize(options={})
36
+ @options = options.clone
37
+
38
+ @options[:subtract_overhead] = true if @options[:subtract_overhead].nil? # Never use ||= with booleans
39
+
26
40
  @measurements = {} # A hash of Measure
27
41
  @current_path = nil
28
42
  @instrumented_methods = {METHOD_TYPE_INSTANCE=>[],METHOD_TYPE_CLASS=>[]}
29
43
  @class_methods = []
30
- #@overhead = nil
31
- #@overhead = Benchmark.measure do
32
- # measure(:a) {}
33
- #end
34
- #@overhead = nil
44
+ @subtract_overhead = @options[:subtract_overhead]
45
+ @@overhead ||= measure_overhead if @subtract_overhead
35
46
  @measurements = {} # A hash of Measure
36
47
  end
37
48
 
49
+ def overhead
50
+ if @subtract_overhead
51
+ @@overhead.clone
52
+ else
53
+ Benchmark::Tms.new
54
+ end
55
+ end
56
+
57
+ # Returns the total time - expressed with a Benchmark::Tms object - for all the blocks measures
58
+ def blocks_time
59
+ @measurements[PATH_MEASURES].time if @measurements[PATH_MEASURES]
60
+ end
61
+
62
+ # Returns the total time - expressed with a Benchmark::Tms object - for all the methods measures
63
+ def methods_time
64
+ @measurements[PATH_METHODS].time if @measurements[PATH_METHODS]
65
+ end
66
+
38
67
  # Takes a description and a code block and measures the performance of the block.
39
68
  # It returns the value returned by the block
40
69
  #
@@ -93,11 +122,11 @@ module Perf
93
122
  res=code.call
94
123
  else
95
124
  t = Benchmark.measure { res=code.call }
96
- #t -= @overhead if @overhead
97
- #if t.total>=0 && t.real>=0
98
- m.time += t
99
- root.time += t if root
100
- #end
125
+ t -= @@overhead if @subtract_overhead && @@overhead # Factor out the overhead of measure, if we are asked to do so
126
+ if t.total>=0 && t.real>=0
127
+ m.time += t
128
+ root.time += t if root
129
+ end
101
130
  end
102
131
  ensure
103
132
  @current_path=current_path
@@ -273,6 +302,26 @@ module Perf
273
302
 
274
303
  protected
275
304
 
305
+ # This method measures the overhead of calling "measure" on an instace of Perf::Meter.
306
+ # It will run OVERHEAD_CALC_RUNS measures of an empty block until the total time taken
307
+ # exceeds OVERHEAD_CALC_MIN_TIME.
308
+
309
+ def measure_overhead
310
+ t=Benchmark::Tms.new
311
+ cnt=0
312
+ rep=0
313
+ runs=OVERHEAD_CALC_RUNS
314
+ while t.total<OVERHEAD_CALC_MIN_TIME && rep<OVERHEAD_CALC_MAX_REPETITIONS
315
+ t+=Benchmark.measure do
316
+ runs.times {measure(:a) {}}
317
+ end
318
+ rep += 1 # Count the repetitions
319
+ cnt += runs # Count the total runs
320
+ runs *= 2 # Increases the number of runs to quickly adapt to the speed of the machine
321
+ end
322
+ t/cnt
323
+ end
324
+
276
325
  def set_measurement(path,m)
277
326
  @measurements[path]=m if m.is_a? Perf::Measure
278
327
  end
@@ -5,42 +5,112 @@
5
5
 
6
6
  module Perf
7
7
 
8
- # Simple Perf::Meter factory and singleton management.
8
+ # Very simple Perf::Meter factory and singleton management.
9
+ #
9
10
  # Useful to not have to pass around Perf::Meter objects and still be able to generate stats in various parts of
10
- # the code.
11
+ # the code. For complex situations where you have multiple Perf::Meter objects you might need to consider
12
+ # either creating a factory that fulfills your needs, or create Perf::Meter objects and pass them around or use
13
+ # this factory will well planned out key values so that you won't have conflicts and overrides.
14
+ #
15
+ # MeterFactory works keeping a global cache of Perf::Meter objects by key. Multiple independent parts
16
+ # of your code using this factory could get into trouble and trample on each other if they use the same key, or the
17
+ # default key.
18
+ #
19
+ # Example of usage where it would be inconvenient to pass around the Perf::Meter object from example to function2:
20
+ #
21
+ # def example
22
+ # Perf::MeterFactory.get.measure(:function1)
23
+ # function1()
24
+ # end
25
+ # end
26
+ #
27
+ # def function1()
28
+ # ..
29
+ # function2()
30
+ # ..
31
+ # end
32
+ #
33
+ # def function2()
34
+ # ..
35
+ # Perf::MeterFactory.get.measure(:some_hot_code_in_function2)
36
+ # ...
37
+ # end
38
+ # ..
39
+ # end
40
+ #
11
41
 
12
42
  class MeterFactory
13
43
 
14
44
  DEFAULT_METER = :default
15
45
 
16
- @@perf_meters=nil
46
+ @@perf_meters = nil
47
+ @@new_meter_options = {}
48
+ @@factory_options = {:noop=>false}
17
49
 
18
- # Returns a Perf::Meter with a given key, and creates it lazly if it doesn't exist'.
19
- def self.get(key=DEFAULT_METER)
50
+ # Returns a Perf::Meter with a given key, and creates it lazily if it doesn't exist'.
51
+ # NOTE: The options are set ONLY the first time that get is called on a specific key.
52
+ # After that the options will be ignored!
53
+
54
+ def self.get(key=DEFAULT_METER,new_meter_options=nil)
20
55
  @@perf_meters ||= {}
21
- @@perf_meters[key] ||= Perf::Meter.new
56
+ if !@@factory_options[:noop]
57
+ # Creates a real meter
58
+ @@perf_meters[key] ||= Perf::Meter.new(new_meter_options || @@new_meter_options)
59
+ else
60
+ # If noop is set, creates a no-nop version of the meter, unless a meter with this key has already been
61
+ # created.
62
+ @@perf_meters[key] ||= Perf::NoOpMeter.new
63
+ end
64
+ end
65
+
66
+ # To set options for new meters created by get, when specific options are not passed, you can do so with this
67
+ # method.
68
+
69
+ def self.set_new_meters_options(options)
70
+ @@new_meter_options.merge(options)
71
+ end
72
+
73
+ # Set options for the factory behaviour.
74
+
75
+ def self.set_factory_options(options)
76
+ @@factory_options.merge!(options)
77
+ end
78
+
79
+ # If you use set_new_meters_options, or if you pass options to Perf::MeterFactory.get, you are setting options
80
+ # only for if the meter is created. For this reason you might need to find out if the meter already exist.
81
+
82
+ def exists?(key=DEFAULT_METER)
83
+ !@@perf_meters[key].nil?
22
84
  end
23
85
 
24
86
  # Pushes a Perf::Meter into a key
87
+
25
88
  def self.set_meter(key,meter)
26
89
  @@perf_meters ||= {}
27
90
  @@perf_meters[key]=meter
28
91
  end
29
92
 
30
93
  # Sets the default meter.
94
+
31
95
  def self.set_default(meter)
32
96
  set_meter(DEFAULT_METER,meter)
33
97
  end
34
98
 
99
+ # Returns a hash of existing meters.
100
+
35
101
  def self.all
36
102
  @@perf_meters ||= {}
37
103
  return @@perf_meters.clone
38
104
  end
39
105
 
106
+ # Removes an existing meter from the cache
107
+
40
108
  def self.clear_meter(key=DEFAULT_METER)
41
109
  @@perf_meters.delete(key) if @@perf_meters
42
110
  end
43
111
 
112
+ # Clears the entire cache of meters.
113
+
44
114
  def self.clear_all!
45
115
  @@perf_meters=nil
46
116
  end
@@ -12,7 +12,7 @@ module Perf
12
12
  #
13
13
  class NoOpMeter
14
14
 
15
- def initialize(logger = nil)
15
+ def initialize(options=nil)
16
16
  end
17
17
 
18
18
  def clear
@@ -33,7 +33,7 @@ module Perf
33
33
  def measure_instance_method(klass,method_name)
34
34
  end
35
35
 
36
- def measure_instance_method(klass,method_name)
36
+ def restore_instance_method(klass,method_name)
37
37
  end
38
38
 
39
39
  def restore_all_instance_methods(klass)
@@ -51,14 +51,24 @@ module Perf
51
51
  def restore_all_methods(klass)
52
52
  end
53
53
 
54
- def measure_full_path(path,&code)
55
- yield
54
+ def overhead
55
+ Benchmark::Tms.new
56
+ end
57
+
58
+ # Returns the total time - expressed with a Benchmark::Tms object - for all the blocks measures
59
+ def blocks_time
60
+ Benchmark::Tms.new
61
+ end
62
+
63
+ # Returns the total time - expressed with a Benchmark::Tms object - for all the methods measures
64
+ def methods_time
65
+ Benchmark::Tms.new
56
66
  end
57
67
 
58
68
  def method_missing(method_sym, *arguments, &block)
59
69
  if method_sym.to_s =~ /^report_(.*)$/
60
70
  klass=Object.const_get("Perf").const_get("ReportFormat#{$1.capitalize}")
61
- return klass.new.format(self) if klass
71
+ return nil if klass
62
72
  end
63
73
  super
64
74
  end
@@ -62,7 +62,7 @@ module Perf
62
62
  end
63
63
 
64
64
  # Header
65
- rep << format_header(:title => "measure path", :max_title => max_title,
65
+ rep << format_header(:title => "measure", :max_title => max_title,
66
66
  :percent => "percent",
67
67
  :count => "count", :max_count => max_count,
68
68
  :time => Benchmark::Tms::CAPTION,
data/rubyperf.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rubyperf}
8
- s.version = "1.0.1"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["lpasqualis"]
@@ -43,14 +43,13 @@ Gem::Specification.new do |s|
43
43
  s.homepage = %q{http://github.com/lpasqualis/rubyperf}
44
44
  s.licenses = ["MIT"]
45
45
  s.require_paths = ["lib"]
46
- s.rubygems_version = %q{1.3.6}
46
+ s.rubygems_version = %q{1.4.2}
47
47
  s.summary = %q{rubyperf helps you measure ruby code performance}
48
48
 
49
49
  if s.respond_to? :specification_version then
50
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
50
  s.specification_version = 3
52
51
 
53
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
53
  s.add_development_dependency(%q<shoulda>, [">= 0"])
55
54
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
56
55
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
@@ -14,6 +14,74 @@ require 'perf_test_example'
14
14
 
15
15
  class TestMeterFactory < Test::Unit::TestCase
16
16
 
17
+ def setup()
18
+ Perf::MeterFactory.clear_all!
19
+ end
20
+
21
+ def teardown()
22
+ Perf::MeterFactory.clear_all!
23
+ Perf::MeterFactory.set_factory_options(:noop=>false)
24
+ end
25
+
26
+ def test_noop
27
+ Perf::MeterFactory.set_factory_options(:noop=>true)
28
+ m1=Perf::MeterFactory.get()
29
+ assert m1.is_a? Perf::NoOpMeter
30
+ m1.measure(:something) do
31
+ # ...
32
+ end
33
+ assert m1.report_simple.nil?
34
+ end
35
+
36
+ def test_noop2
37
+ Perf::MeterFactory.set_factory_options(:noop=>true)
38
+ m=Perf::MeterFactory.get()
39
+ assert m.is_a? Perf::NoOpMeter
40
+ m.measure(:string_operations) do
41
+ m.measure(:ciao) do
42
+ 10.times do; "CIAO"*100; end
43
+ end
44
+ end
45
+ m.measure(:string_operations) do
46
+ m.measure(:help) do
47
+ 10.times do; "HELP"*100; end
48
+ end
49
+ end
50
+ m.measure(:emtpy_loop) do
51
+ 500.times do; end;
52
+ end
53
+ m.measure(:rough_overhead_x10000) do
54
+ 10.times do
55
+ m.measure(:block_1) do
56
+ m.measure(:block_1_1) do
57
+ end
58
+ m.measure(:block_1_2) do
59
+ m.measure(:block_1_2_1) do
60
+ end
61
+ m.measure(:block_1_2_2) do
62
+ end
63
+ m.measure(:block_1_2_3) do
64
+ assert_equal false,m.measure_result(:bool_exp_1_2_3) { false }
65
+ m.measure_result(:bool_exp_1_2_3) { true }
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ m.measure(:empty) do
73
+ end
74
+ m.measure_result("test") { "something" }
75
+ m.measure_result("test") { false }
76
+ m.measure_result("test") { false }
77
+
78
+ m.method_meters(Array,[:sort,:reverse],[:new]) do
79
+ Array.new(1000000,"abc").reverse.sort
80
+ end
81
+ assert_equal 123,m.measure(:blah) {123}
82
+ assert m.report_simple.nil?
83
+ end
84
+
17
85
  def test_basic
18
86
  Perf::MeterFactory.clear_all!
19
87
  m1=Perf::MeterFactory.get()
@@ -107,25 +107,8 @@ class TestPerfMeter < Test::Unit::TestCase
107
107
  assert PerfTestExample.methods.sort == cmethods
108
108
  assert PerfTestExample.new.methods.sort == imethods
109
109
 
110
- assert_equal ['\methods,0',
111
- '\methods\#<Class:PerfTestExample>.static_method,1',
112
- '\methods\PerfTestExample.test,1',
113
- '\methods\PerfTestExample.test_np,1'],
114
- m.report_list_of_measures
115
-
116
110
  assert_equal 6,m.report_simple.length
117
111
  assert_equal 6,m.report_html.length
118
- end
119
-
120
- def test_method_metering
121
- m=Perf::Meter.new
122
- m.method_meters(PerfTestExample,[:test,:test_np],[:static_method]) do
123
- a=PerfTestExample.new
124
- a.test(1,2,3)
125
- a.test_np
126
- PerfTestExample.static_method
127
- end
128
-
129
112
  assert_equal ['\methods,0',
130
113
  '\methods\#<Class:PerfTestExample>.static_method,1',
131
114
  '\methods\PerfTestExample.test,1',
@@ -285,13 +268,34 @@ class TestPerfMeter < Test::Unit::TestCase
285
268
  def test_overhead
286
269
  runs=1_000
287
270
  a=(1..100_000).to_a
288
- m=Perf::Meter.new
289
- b1=Benchmark.measure { runs.times { m.measure(:a) { a.reverse! } } }
290
- b2=Benchmark.measure { runs.times { a.reverse! } }
271
+ m_no_overhead=Perf::Meter.new(:subtract_overhead=>true)
272
+ b1_no_overhead=Benchmark.measure { runs.times { m_no_overhead.measure(:a) { a.reverse! } } }
273
+ b2_no_overhead=Benchmark.measure { runs.times { a.reverse! } }
274
+
275
+ m_yes_overhead=Perf::Meter.new(:subtract_overhead=>false)
276
+ b1_yes_overhead=Benchmark.measure { runs.times { m_yes_overhead.measure(:a) { a.reverse! } } }
277
+ b2_yes_overhead=Benchmark.measure { runs.times { a.reverse! } }
278
+
279
+
291
280
  assert_equal ['\blocks,0',
292
281
  '\blocks\a,1000'],
293
- m.report_list_of_measures
294
- # Here we should assert if the overhead is > a certain percentage.
295
- # puts "Measurement overhead x 1000 = #{b1-b2}"
282
+ m_no_overhead.report_list_of_measures
283
+
284
+ assert_equal m_no_overhead.report_list_of_measures,
285
+ m_yes_overhead.report_list_of_measures
286
+
287
+ # TODO: find the magic assert that ensures that the overhead calculation is correct. Ensure that such assert
288
+ # is machine independent and that will pass the test of time (new hardware getting faster and faster)
289
+
290
+ #calculated_overhead_1= (b1_no_overhead-b2_no_overhead)/runs
291
+ #calculated_overhead_2= (b1_yes_overhead-b2_yes_overhead)/runs
292
+ #
293
+ #puts (calculated_overhead_1-m_no_overhead.overhead)
294
+ #puts (calculated_overhead_2-m_yes_overhead.overhead)
295
+ #
296
+ #puts m_no_overhead.report_simple
297
+ #puts m_yes_overhead.report_simple
298
+ #
299
+ #assert m_no_overhead.blocks_time.total > m_yes_overhead.blocks_time.total
296
300
  end
297
301
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyperf
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 19
5
+ prerelease:
5
6
  segments:
6
7
  - 1
7
- - 0
8
8
  - 1
9
- segments_generated: true
10
- version: 1.0.1
9
+ - 0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - lpasqualis
@@ -20,60 +20,64 @@ default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  prerelease: false
23
- type: :development
24
- name: shoulda
25
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
28
+ hash: 3
29
29
  segments:
30
30
  - 0
31
- segments_generated: true
32
31
  version: "0"
33
- requirement: *id001
32
+ type: :development
33
+ name: shoulda
34
+ version_requirements: *id001
34
35
  - !ruby/object:Gem::Dependency
35
36
  prerelease: false
36
- type: :development
37
- name: bundler
38
- version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
39
  requirements:
40
40
  - - ~>
41
41
  - !ruby/object:Gem::Version
42
+ hash: 23
42
43
  segments:
43
44
  - 1
44
45
  - 0
45
46
  - 0
46
- segments_generated: true
47
47
  version: 1.0.0
48
- requirement: *id002
48
+ type: :development
49
+ name: bundler
50
+ version_requirements: *id002
49
51
  - !ruby/object:Gem::Dependency
50
52
  prerelease: false
51
- type: :development
52
- name: jeweler
53
- version_requirements: &id003 !ruby/object:Gem::Requirement
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
54
55
  requirements:
55
56
  - - ~>
56
57
  - !ruby/object:Gem::Version
58
+ hash: 7
57
59
  segments:
58
60
  - 1
59
61
  - 6
60
62
  - 4
61
- segments_generated: true
62
63
  version: 1.6.4
63
- requirement: *id003
64
+ type: :development
65
+ name: jeweler
66
+ version_requirements: *id003
64
67
  - !ruby/object:Gem::Dependency
65
68
  prerelease: false
66
- type: :development
67
- name: rcov
68
- version_requirements: &id004 !ruby/object:Gem::Requirement
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
69
71
  requirements:
70
72
  - - ">="
71
73
  - !ruby/object:Gem::Version
74
+ hash: 3
72
75
  segments:
73
76
  - 0
74
- segments_generated: true
75
77
  version: "0"
76
- requirement: *id004
78
+ type: :development
79
+ name: rcov
80
+ version_requirements: *id004
77
81
  description: Used to easily measure the performance of blocks of Ruby code, expressions and methods; provides reporting in various formats
78
82
  email: lpasqualis@gmail.com
79
83
  executables: []
@@ -116,25 +120,27 @@ rdoc_options: []
116
120
  require_paths:
117
121
  - lib
118
122
  required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
119
124
  requirements:
120
125
  - - ">="
121
126
  - !ruby/object:Gem::Version
127
+ hash: 3
122
128
  segments:
123
129
  - 0
124
- segments_generated: true
125
130
  version: "0"
126
131
  required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
127
133
  requirements:
128
134
  - - ">="
129
135
  - !ruby/object:Gem::Version
136
+ hash: 3
130
137
  segments:
131
138
  - 0
132
- segments_generated: true
133
139
  version: "0"
134
140
  requirements: []
135
141
 
136
142
  rubyforge_project:
137
- rubygems_version: 1.3.6
143
+ rubygems_version: 1.4.2
138
144
  signing_key:
139
145
  specification_version: 3
140
146
  summary: rubyperf helps you measure ruby code performance