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.
- data/HISTORY +10 -0
- data/README +74 -14
- data/ext/hitimes_interval.c +6 -3
- data/ext/hitimes_stats.c +31 -8
- data/gemspec.rb +4 -2
- data/lib/hitimes.rb +5 -1
- data/lib/hitimes/metric.rb +74 -0
- data/lib/hitimes/stats.rb +26 -1
- data/lib/hitimes/timed_metric.rb +181 -0
- data/lib/hitimes/timed_value_metric.rb +240 -0
- data/lib/hitimes/value_metric.rb +69 -0
- data/lib/hitimes/version.rb +23 -8
- data/lib/hitimes_ext.so +0 -0
- data/spec/metric_spec.rb +30 -0
- data/spec/stats_spec.rb +30 -2
- data/spec/timed_metric_spec.rb +155 -0
- data/spec/timed_value_metric_spec.rb +172 -0
- data/spec/value_metric_spec.rb +110 -0
- data/spec/version_spec.rb +6 -0
- data/tasks/config.rb +1 -1
- metadata +27 -8
- data/lib/hitimes/timer.rb +0 -223
- data/spec/timer_spec.rb +0 -105
@@ -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
|
data/lib/hitimes/version.rb
CHANGED
@@ -9,9 +9,14 @@ module Hitimes
|
|
9
9
|
#
|
10
10
|
module Version
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
37
|
-
|
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
|
data/spec/metric_spec.rb
ADDED
@@ -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 '
|
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
|