hitimes 0.4.1-x86-mswin32-60 → 1.0.0-x86-mswin32-60

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.
@@ -0,0 +1,240 @@
1
+ #--
2
+ # Copyright (c) 2008, 2009 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+
6
+ require 'hitimes'
7
+
8
+ module Hitimes
9
+ #
10
+ # A TimedValueMetric holds the metrics on how long it takes to do a batch of something.
11
+ # something. For measuring how long a method takes to operate on N items.
12
+ #
13
+ # tm = TimedValueMetric.new( 'my-batch-method' )
14
+ #
15
+ # 42.times do
16
+ # tm.start
17
+ # number_of_items_processed = do_something
18
+ # tm.stop( number_of_items_processed )
19
+ # end
20
+ #
21
+ # puts "#{ tm.name } operated at a rate of #{ tm.rate } calls per second"
22
+ #
23
+ # TimedValueMetric combines the usefulness of a ValueMetric and a TimedMetric.
24
+ # The stats are available for both the time it took to do the operation and
25
+ # the sizes of the batches that were run.
26
+ #
27
+ # A TimedValueMetric keeps track of both the time it took to do an operation
28
+ # and the size of the batch that was operated on. These metrics are kept
29
+ # separately as +timed_stats+ and +value_stats+ accessors.
30
+ #
31
+ class TimedValueMetric < Metric
32
+ # holds all the Timed statistics
33
+ attr_reader :timed_stats
34
+
35
+ # holds all the Value statistics
36
+ attr_reader :value_stats
37
+
38
+ class << TimedValueMetric
39
+ #
40
+ # :call-seq:
41
+ # TimedValueMetric.now( 'name' ) -> TimedValueMetric
42
+ #
43
+ # Return a TimedValueMetric that has been started
44
+ #
45
+ def now( name, additional_data = {} )
46
+ t = TimedValueMetric.new( name, additional_data )
47
+ t.start
48
+ return t
49
+ end
50
+ end
51
+
52
+ #
53
+ # :call-seq:
54
+ # TimedValueMetric.new( 'name') -> TimedValueMetric
55
+ # TimedValueMetric.new( 'name', 'other' => 'data') -> TimedValueMetric
56
+ #
57
+ # Create a new TimedValueMetric giving it a name and additional data.
58
+ # +additional_data+ may be anything that follows the +to_hash+ protocol
59
+ #
60
+ def initialize( name, additional_data = {} )
61
+ super( name, additional_data )
62
+ @current_interval = nil
63
+ @timed_stats = Stats.new
64
+ @value_stats = Stats.new
65
+ end
66
+
67
+ #
68
+ # :call-seq:
69
+ # timed_value_metric.current_interval -> Interval
70
+ #
71
+ # Return the current interval, if one doesn't exist create one.
72
+ #
73
+ def current_interval
74
+ @current_interval ||= Interval.new
75
+ end
76
+
77
+
78
+ #
79
+ # :call-seq:
80
+ # timed_value_metric.running? -> true or false
81
+ #
82
+ # return whether or not the metric is currently timing something.
83
+ #
84
+ def running?
85
+ current_interval.running?
86
+ end
87
+
88
+ #
89
+ # :call-seq:
90
+ # timed_value_metric.start -> nil
91
+ #
92
+ # Start the current timer, if the current timer is already started, then
93
+ # this is a noop.
94
+ #
95
+ def start
96
+ current_interval.start unless running?
97
+ @sampling_start_time ||= self.utc_microseconds()
98
+ nil
99
+ end
100
+
101
+ #
102
+ # :call-seq:
103
+ # timed_value_metric.stop( count ) -> Float or nil
104
+ #
105
+ # Stop the current metric. The +count+ parameter must be a
106
+ # value to update to the _value_ portion of the TimedValueMetric. Generally
107
+ # this is probably the number of things that were operated upon since
108
+ # +start+ was invoked.
109
+ #
110
+ # This updates both the +value_stats+ and +timed_stats+ stats and removes
111
+ # the current interval. If the metric is stopped then the duration of the
112
+ # last Interval is returned. If the metric was already stopped before this
113
+ # call, then false is returned and no stats are updated.
114
+ #
115
+ #
116
+ def stop( value )
117
+ if running? then
118
+ d = current_interval.stop
119
+ @current_interval = nil
120
+ @sampling_stop_time = self.utc_microseconds()
121
+ @timed_stats.update( d )
122
+ @value_stats.update( value )
123
+ return d
124
+ end
125
+ return false
126
+ end
127
+
128
+ #
129
+ # :call-seq:
130
+ # timed_value_metric.measure( value ) { ... } -> Object
131
+ #
132
+ # Measure the execution of a block and add those stats to the running stats.
133
+ # The return value is the return value of the block. A value must be passed
134
+ # into +measure+ to update the +value_stats+ portion of the TimedValueMetric.
135
+ #
136
+ def measure( value, &block )
137
+ return_value = nil
138
+ begin
139
+ start
140
+ return_value = yield
141
+ ensure
142
+ stop( value )
143
+ end
144
+ return return_value
145
+ end
146
+
147
+ #
148
+ # :call-seq:
149
+ # timed_value_metric.split( value ) -> Float
150
+ #
151
+ # Split the current metric. Essentially, mark a split time. This means
152
+ # stop the current interval, with the givein +value+ and create a new
153
+ # interval, but make sure that the new interval lines up exactly, timewise,
154
+ # behind the previous interval.
155
+ #
156
+ # If the metric is running, then split returns the duration of the previous
157
+ # interval, i.e. the split-time. If the metric is not running, nothing
158
+ # happens, no stats are updated, and false is returned.
159
+ #
160
+ #
161
+ def split( value )
162
+ if running? then
163
+ next_interval = current_interval.split
164
+ d = current_interval.duration
165
+ @timed_stats.update( d )
166
+ @value_stats.update( value )
167
+ @current_interval = next_interval
168
+ return d
169
+ end
170
+ return false
171
+ end
172
+
173
+ #
174
+ # :call-seq:
175
+ # timed_value_metric.duration -> Float
176
+ #
177
+ # The duration of measured time from the metric.
178
+ #
179
+ def duration
180
+ @timed_stats.sum
181
+ end
182
+
183
+ #
184
+ # :call-seq:
185
+ # timed_value_metric.unit_count -> Float
186
+ #
187
+ # The sum of all values passed to +stop+ or +skip+ or +measure+
188
+ #
189
+ def unit_count
190
+ @value_stats.sum
191
+ end
192
+
193
+ #
194
+ # :call-seq:
195
+ # timed_value_metric.rate -> Float
196
+ #
197
+ # Rate in the context of the TimedValueMetric is different than the
198
+ # TimedMetric. In the TimedValueMetric, each measurement of time is
199
+ # associated with a quantity of things done during that unit of time. So
200
+ # the +rate+ for a TimedValueMetric is the (sum of all quantities sampled) /
201
+ # ( sum of all durations measured )
202
+ #
203
+ # For example, say you were measuring, using a TimedValueMetric batch jobs
204
+ # that had individual units of work.
205
+ #
206
+ # tvm = TimedValueMetric.new( 'some-batch' )
207
+ # tvm.start
208
+ # # process a batch of 12 units
209
+ # duration1 = tvm.stop( 12 )
210
+ #
211
+ # tvm.start
212
+ # # process a larger batch of 42 units
213
+ # duration2 = tvm.stop( 42 )
214
+ #
215
+ # At this point the rate of units per second is calculated as ( 12 + 42 ) / ( duration1 + duration2 )
216
+ #
217
+ # some_batch_rate = tvm.rate # returns ( 34 / ( duration1+duration2 ) )
218
+ #
219
+ def rate
220
+ @value_stats.sum / @timed_stats.sum
221
+ end
222
+
223
+ #
224
+ # :call-seq:
225
+ # metric.to_hash -> Hash
226
+ #
227
+ # Convert the metric to a hash
228
+ #
229
+ def to_hash
230
+ h = super
231
+ h['timed_stats'] = @timed_stats.to_hash
232
+ h['value_stats'] = @value_stats.to_hash( Stats::STATS - %w[ rate ] )
233
+ h['rate'] = self.rate
234
+ h['unit_count'] = self.unit_count
235
+ return h
236
+ end
237
+
238
+
239
+ end
240
+ end
@@ -0,0 +1,69 @@
1
+ #--
2
+ # Copyright (c) 2008, 2009 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+
6
+ require 'forwardable'
7
+ require 'hitimes'
8
+ module Hitimes
9
+ #
10
+ # A ValueMetric holds the data from measuring a single value over a period of
11
+ # time. In most cases this may be a single measurement at a single point in
12
+ # time.
13
+ #
14
+ # A good example of a ValueMetric is measuring the number of items in a queue.
15
+ #
16
+ # A ValueMetric contains a Stats object, therefore ValueMetric has +count+, +max+,
17
+ # +mean+, +min+, +stddev+, +sum+, +sumsq+ methods that delegate to that Stats
18
+ # object for convenience.
19
+ #
20
+ class ValueMetric < Metric
21
+
22
+ # holds all the statistics
23
+ attr_reader :stats
24
+
25
+ #
26
+ # :call-seq:
27
+ # ValueMetric.new( 'my_metric' ) -> ValueMetric
28
+ # ValueMetric.new( 'my_metric', 'foo' => 'bar', 'this' => 42 ) -> ValueMetric
29
+ #
30
+ # Create a new ValueMetric giving it a name and additional data.
31
+ # +additional_data+ may be anything that follows the +to_hash+ protocol.
32
+ #
33
+ def initialize( name, additional_data = {} )
34
+ super( name, additional_data )
35
+ @stats = Stats.new
36
+ end
37
+
38
+ #
39
+ # :call-seq:
40
+ # metric.measure( value ) -> Float
41
+ #
42
+ # Give the +value+ as the measurement to the metric. The value is returned
43
+ #
44
+ def measure( value )
45
+ now = self.utc_microseconds()
46
+ @sampling_start_time ||= now
47
+ @sampling_stop_time = now
48
+ @stats.update( value )
49
+ end
50
+
51
+ #
52
+ # :call-seq:
53
+ # metric.to_hash -> Hash
54
+ #
55
+ # Convert the metric to a hash
56
+ #
57
+ def to_hash
58
+ h = super
59
+ (Stats::STATS - %w[ rate ]).each do |s|
60
+ h[s] = self.send( s )
61
+ end
62
+ return h
63
+ end
64
+
65
+ # forward appropriate calls directly to the stats object
66
+ extend Forwardable
67
+ def_delegators :@stats, :count, :sum, :max, :mean, :min, :stddev, :sum, :sumsq
68
+ end
69
+ end
@@ -9,9 +9,14 @@ module Hitimes
9
9
  #
10
10
  module Version
11
11
 
12
- MAJOR = 0
13
- MINOR = 4
14
- BUILD = 1
12
+ # Major version number
13
+ MAJOR = 1
14
+
15
+ # Minor version number
16
+ MINOR = 0
17
+
18
+ # Build number
19
+ BUILD = 0
15
20
 
16
21
  #
17
22
  # :call-seq:
@@ -19,24 +24,34 @@ module Hitimes
19
24
  #
20
25
  # Return the version as an array of Integers
21
26
  #
22
- def to_a
27
+ def self.to_a
23
28
  [MAJOR, MINOR, BUILD]
24
29
  end
25
30
 
26
31
  #
27
32
  # :call-seq:
28
- # Version.to_s -> MAJOR.MINOR.BUILD
33
+ # Version.to_s -> "MAJOR.MINOR.BUILD"
29
34
  #
30
35
  # Return the version as a String with dotted notation
31
36
  #
32
- def to_s
37
+ def self.to_s
33
38
  to_a.join(".")
34
39
  end
35
40
 
36
- module_function :to_a
37
- module_function :to_s
41
+ #
42
+ # :call-seq:
43
+ # Version.to_hash -> { :major => ..., :minor => ..., :build => ... }
44
+ #
45
+ # Return the version as a Hash
46
+ #
47
+ def self.to_hash
48
+ { :major => MAJOR, :minor => MINOR, :build => BUILD }
49
+ end
38
50
 
51
+ # The Version in MAJOR.MINOR.BUILD dotted notation
39
52
  STRING = Version.to_s
40
53
  end
54
+
55
+ # The Version in MAJOR.MINOR.BUILD dotted notation
41
56
  VERSION = Version.to_s
42
57
  end
data/lib/hitimes_ext.so CHANGED
Binary file
@@ -0,0 +1,30 @@
1
+ require File.expand_path( File.join( File.dirname( __FILE__ ), "spec_helper.rb" ) )
2
+
3
+ require 'hitimes/metric'
4
+
5
+ describe Hitimes::Metric do
6
+ before( :each ) do
7
+ @metric = Hitimes::Metric.new( "testing" )
8
+ end
9
+
10
+ it 'has a name' do
11
+ @metric.name.should == "testing"
12
+ end
13
+
14
+ it "has associated data from initialization" do
15
+ m = Hitimes::Metric.new( "more-data", 'foo' => 'bar', 'this' => 'that' )
16
+ m.additional_data['foo'].should == 'bar'
17
+ m.additional_data['this'].should == 'that'
18
+
19
+ m = Hitimes::Metric.new( "more-data", { 'foo' => 'bar', 'this' => 'that' } )
20
+ m.additional_data['foo'].should == 'bar'
21
+ m.additional_data['this'].should == 'that'
22
+ end
23
+
24
+ it "initially has no sampling times" do
25
+ @metric.sampling_start_time.should == nil
26
+ @metric.sampling_stop_time.should == nil
27
+ end
28
+ end
29
+
30
+
data/spec/stats_spec.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require File.expand_path( File.join( File.dirname( __FILE__ ), "spec_helper.rb" ) )
2
2
 
3
- require 'hitimes_ext'
3
+ require 'hitimes/stats'
4
+ require 'json'
4
5
 
5
6
  describe Hitimes::Stats do
6
7
  before( :each ) do
@@ -46,6 +47,10 @@ describe Hitimes::Stats do
46
47
  @full_stats.stddev.should == 1.0
47
48
  end
48
49
 
50
+ it "calculates the sum of squares " do
51
+ @full_stats.sumsq.should == 14.0
52
+ end
53
+
49
54
  describe "#to_hash " do
50
55
  it "converts to a Hash" do
51
56
  h = @full_stats.to_hash
@@ -66,7 +71,30 @@ describe Hitimes::Stats do
66
71
  it "raises NoMethodError if an invalid stat is used" do
67
72
  lambda { @full_stats.to_hash( "wibble" ) }.should raise_error( NoMethodError )
68
73
  end
69
-
70
74
  end
71
75
 
76
+ describe "#to_json" do
77
+ it "converts to a json string" do
78
+ j = @full_stats.to_json
79
+ h = JSON.parse( j )
80
+ h.size.should == ::Hitimes::Stats::STATS.size
81
+ h.keys.sort.should == ::Hitimes::Stats::STATS
82
+ end
83
+
84
+ it "converts to a limited Hash if given arguments" do
85
+ j = @full_stats.to_json( "min", "max", "mean" )
86
+ h = JSON.parse( j )
87
+ h.size.should == 3
88
+ h.keys.sort.should == %w[ max mean min ]
89
+
90
+ j = @full_stats.to_json( %w[ count rate ] )
91
+ h = JSON.parse( j )
92
+ h.size.should == 2
93
+ h.keys.sort.should == %w[ count rate ]
94
+ end
95
+
96
+ it "raises NoMethodError if an invalid stat is used" do
97
+ lambda { @full_stats.to_json( "wibble" ) }.should raise_error( NoMethodError )
98
+ end
99
+ end
72
100
  end