hitimes 1.3.1 → 2.0.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.
@@ -11,22 +11,7 @@ module Hitimes
11
11
  # Ruby the Stats object is already threadsafe, so there is no need to use
12
12
  # MutexedStats.
13
13
  #
14
- class MutexedStats < Stats
15
- def initialize
16
- @mutex = Mutex.new
17
- end
18
-
19
- # call-seq:
20
- # mutex_stat.update( val ) -> nil
21
- #
22
- # Update the running stats with the new value in a threadsafe manner.
23
- #
24
- def update( value )
25
- @mutex.synchronize do
26
- super( value )
27
- end
28
- end
29
- end
14
+ MutexedStats = Stats
30
15
  end
31
16
 
32
17
 
@@ -4,11 +4,109 @@
4
4
  #++
5
5
 
6
6
  require 'stringio'
7
+ require 'thread'
7
8
  module Hitimes
9
+ #
10
+ # The Stats class encapulsates capturing and reporting statistics. It is
11
+ # modeled after the RFuzz::Sampler class, but implemented in C. For general use
12
+ # you allocate a new Stats object, and then update it with new values. The
13
+ # Stats object will keep track of the _min_, _max_, _count_, _sum_ and _sumsq_
14
+ # and when you want you may also retrieve the _mean_, _stddev_ and _rate_.
15
+ #
16
+ # this contrived example shows getting a list of all the files in a directory
17
+ # and running stats on file sizes.
18
+ #
19
+ # s = Hitimes::Stats.new
20
+ # dir = ARGV.shift || Dir.pwd
21
+ # Dir.entries( dir ).each do |entry|
22
+ # fs = File.stat( entry )
23
+ # if fs.file? then
24
+ # s.update( fs.size )
25
+ # end
26
+ # end
27
+ #
28
+ # %w[ count min max mean sum stddev rate ].each do |m|
29
+ # puts "#{m.rjust(6)} : #{s.send( m ) }"
30
+ # end
31
+ #
8
32
  class Stats
9
33
  # A list of the available stats
10
34
  STATS = %w[ count max mean min rate stddev sum sumsq ]
11
35
 
36
+ attr_reader :min
37
+ attr_reader :max
38
+ attr_reader :count
39
+ attr_reader :sum
40
+ attr_reader :sumsq
41
+
42
+ def initialize
43
+ @mutex = Mutex.new
44
+ @min = Float::INFINITY
45
+ @max = -Float::INFINITY
46
+ @count = 0
47
+ @sum = 0.0
48
+ @sumsq = 0.0
49
+ end
50
+
51
+ # call-seq:
52
+ # stat.update( val ) -> val
53
+ #
54
+ # Update the running stats with the new value.
55
+ # Return the input value.
56
+ def update(value)
57
+ @mutex.synchronize do
58
+ @min = (value < @min) ? value : @min
59
+ @max = (value > @max) ? value : @max
60
+
61
+ @count += 1
62
+ @sum += value
63
+ @sumsq += (value * value)
64
+ end
65
+
66
+ return value
67
+ end
68
+
69
+ # call-seq:
70
+ # stat.mean -> Float
71
+ #
72
+ # Return the arithmetic mean of the values put into the Stats object. If no
73
+ # values have passed through the stats object then 0.0 is returned;
74
+ def mean
75
+ return 0.0 if @count.zero?
76
+ return @sum / @count
77
+ end
78
+
79
+ # call-seq:
80
+ # stat.rate -> Float
81
+ #
82
+ # Return the +count+ divided by +sum+.
83
+ #
84
+ # In many cases when Stats#update( _value_ ) is called, the _value_ is a unit
85
+ # of time, typically seconds or microseconds. #rate is a convenience for those
86
+ # times. In this case, where _value_ is a unit if time, then count divided by
87
+ # sum is a useful value, i.e. +something per unit of time+.
88
+ #
89
+ # In the case where _value_ is a non-time related value, then the value
90
+ # returned by _rate_ is not really useful.
91
+ #
92
+ def rate
93
+ return 0.0 if @sum.zero?
94
+ return @count / @sum
95
+ end
96
+
97
+ #
98
+ # call-seq:
99
+ # stat.stddev -> Float
100
+ #
101
+ # Return the standard deviation of all the values that have passed through the
102
+ # Stats object. The standard deviation has no meaning unless the count is > 1,
103
+ # therefore if the current _stat.count_ is < 1 then 0.0 will be returned;
104
+ #
105
+ def stddev
106
+ return 0.0 unless @count > 1
107
+ Math.sqrt((@sumsq - ((@sum * @sum)/@count)) / (@count - 1))
108
+ end
109
+
12
110
  #
13
111
  # call-seq:
14
112
  # stat.to_hash -> Hash
@@ -53,6 +151,5 @@ module Hitimes
53
151
  s.print "}"
54
152
  return s.string
55
153
  end
56
-
57
154
  end
58
155
  end
@@ -4,5 +4,5 @@
4
4
  #++
5
5
 
6
6
  module Hitimes
7
- VERSION = "1.3.1"
7
+ VERSION = "2.0.0"
8
8
  end
@@ -5,20 +5,20 @@ describe Hitimes do
5
5
  d = Hitimes.measure do
6
6
  sleep 0.2
7
7
  end
8
- d.must_be_close_to(0.2, 0.002)
8
+ _(d).must_be_close_to(0.2, 0.002)
9
9
  end
10
10
 
11
11
  it "raises an error if measure is called with no block" do
12
- lambda{ Hitimes.measure }.must_raise( Hitimes::Error )
12
+ _(lambda{ Hitimes.measure }).must_raise( Hitimes::Error )
13
13
  end
14
14
 
15
15
  it "has the raw instant value" do
16
16
  v = Hitimes.raw_instant
17
- v.must_be :>, 0
17
+ _(v).must_be :>, 0
18
18
  end
19
19
 
20
20
  it "has access to the instant conversion factor" do
21
21
  f = Hitimes::INSTANT_CONVERSION_FACTOR
22
- f.must_be :>, 0
22
+ _(f).must_be :>, 0
23
23
  end
24
24
  end
@@ -3,54 +3,54 @@ require "spec_helper"
3
3
  describe Hitimes::Interval do
4
4
  it "raises an error if duration is called on a non-started interval" do
5
5
  i = Hitimes::Interval.new
6
- lambda{ i.duration }.must_raise( Hitimes::Error, /\AAttempt to report a duration on an interval that has not started\Z/ )
6
+ _(lambda{ i.duration }).must_raise( Hitimes::Error, /\AAttempt to report a duration on an interval that has not started\Z/ )
7
7
  end
8
8
 
9
9
  it "raises an error if stop is called on a non-started interval" do
10
10
  i = Hitimes::Interval.new
11
- lambda { i.stop }.must_raise( Hitimes::Error, /\AAttempt to stop an interval that has not started\Z/ )
11
+ _(lambda { i.stop }).must_raise( Hitimes::Error, /\AAttempt to stop an interval that has not started\Z/ )
12
12
  end
13
13
 
14
14
  it "knows if it has been started" do
15
15
  i = Hitimes::Interval.new
16
- i.started?.must_equal false
16
+ _(i.started?).must_equal false
17
17
 
18
18
  i.start
19
- i.started?.must_equal true
19
+ _(i.started?).must_equal true
20
20
  end
21
21
 
22
22
  it "knows if it has been stopped" do
23
23
  i = Hitimes::Interval.new
24
24
  i.start
25
- i.stopped?.must_equal false
25
+ _(i.stopped?).must_equal false
26
26
  i.stop
27
- i.stopped?.must_equal true
27
+ _(i.stopped?).must_equal true
28
28
  end
29
29
 
30
30
  it "knows if it is currently running" do
31
31
  i = Hitimes::Interval.new
32
- i.running?.must_equal false
32
+ _(i.running?).must_equal false
33
33
  i.start
34
- i.running?.must_equal true
34
+ _(i.running?).must_equal true
35
35
  i.stop
36
- i.running?.must_equal false
36
+ _(i.running?).must_equal false
37
37
  end
38
38
 
39
39
  it "can time a block of code" do
40
40
  d = Hitimes::Interval.measure do
41
41
  sleep 0.2
42
42
  end
43
- d.must_be_close_to(0.2, 0.002)
43
+ _(d).must_be_close_to(0.2, 0.002)
44
44
  end
45
45
 
46
46
  it "raises an error if measure is called with no block" do
47
- lambda{ Hitimes::Interval.measure }.must_raise( Hitimes::Error, /\ANo block given to Interval.measure\Z/ )
47
+ _(lambda{ Hitimes::Interval.measure }).must_raise( Hitimes::Error, /\ANo block given to Interval.measure\Z/ )
48
48
  end
49
49
 
50
50
  it "creates an interval via #now" do
51
51
  i = Hitimes::Interval.now
52
- i.started?.must_equal true
53
- i.stopped?.must_equal false
52
+ _(i.started?).must_equal true
53
+ _(i.stopped?).must_equal false
54
54
  end
55
55
 
56
56
  it "calling duration multiple times returns successivly grater durations" do
@@ -58,33 +58,33 @@ describe Hitimes::Interval do
58
58
  i.start
59
59
  y = i.duration
60
60
  z = i.duration
61
- z.must_be :>, y
61
+ _(z).must_be :>, y
62
62
  end
63
63
 
64
64
  it "calling start multiple times on has no effect after the first call" do
65
65
  i = Hitimes::Interval.new
66
- i.start.must_equal true
66
+ _(i.start).must_equal true
67
67
  x = i.start_instant
68
- i.start_instant.must_be :>, 0
69
- i.start.must_equal false
70
- x.must_equal i.start_instant
68
+ _(i.start_instant).must_be :>, 0
69
+ _(i.start).must_equal false
70
+ _(x).must_equal i.start_instant
71
71
  end
72
72
 
73
73
  it "returns the duration on the first call to stop" do
74
74
  i = Hitimes::Interval.now
75
75
  d = i.stop
76
- d.must_be_instance_of( Float )
76
+ _(d).must_be_instance_of( Float )
77
77
  end
78
78
 
79
79
  it "calling stop multiple times on has no effect after the first call" do
80
80
  i = Hitimes::Interval.new
81
- i.start.must_equal true
81
+ _(i.start).must_equal true
82
82
  i.stop
83
83
 
84
84
  x = i.stop_instant
85
- i.stop_instant.must_be :>, 0
86
- i.stop.must_equal false
87
- x.must_equal i.stop_instant
85
+ _(i.stop_instant).must_be :>, 0
86
+ _(i.stop).must_equal false
87
+ _(x).must_equal i.stop_instant
88
88
 
89
89
  end
90
90
 
@@ -93,26 +93,26 @@ describe Hitimes::Interval do
93
93
  i.start
94
94
  x = i.stop
95
95
  y = i.duration
96
- i.stop.must_equal false
96
+ _(i.stop).must_equal false
97
97
 
98
98
  z = i.duration
99
99
 
100
- x.must_equal y
101
- x.must_equal z
100
+ _(x).must_equal y
101
+ _(x).must_equal z
102
102
 
103
- y.must_equal z
103
+ _(y).must_equal z
104
104
  end
105
105
 
106
106
  it "can return how much time has elapsed from the start without stopping the interval" do
107
107
  i = Hitimes::Interval.new
108
108
  i.start
109
109
  x = i.duration_so_far
110
- i.running?.must_equal true
110
+ _(i.running?).must_equal true
111
111
  y = i.duration_so_far
112
112
  i.stop
113
- x.must_be :<, y
114
- x.must_be :<, i.duration
115
- y.must_be :<, i.duration
113
+ _(x).must_be :<, y
114
+ _(x).must_be :<, i.duration
115
+ _(y).must_be :<, i.duration
116
116
  end
117
117
 
118
118
  describe "#split" do
@@ -121,14 +121,14 @@ describe Hitimes::Interval do
121
121
  i = Hitimes::Interval.new
122
122
  i.start
123
123
  i2 = i.split
124
- i.object_id.wont_equal i2.object_id
124
+ _(i.object_id).wont_equal i2.object_id
125
125
  end
126
126
 
127
127
  it "with the stop instant equivialent to the previous Interval's start instant" do
128
128
  i = Hitimes::Interval.new
129
129
  i.start
130
130
  i2 = i.split
131
- i.stop_instant.must_equal i2.start_instant
131
+ _(i.stop_instant).must_equal i2.start_instant
132
132
  end
133
133
  end
134
134
 
@@ -6,22 +6,22 @@ describe Hitimes::Metric do
6
6
  end
7
7
 
8
8
  it 'has a name' do
9
- @metric.name.must_equal "testing"
9
+ _(@metric.name).must_equal "testing"
10
10
  end
11
11
 
12
12
  it "has associated data from initialization" do
13
13
  m = Hitimes::Metric.new( "more-data", 'foo' => 'bar', 'this' => 'that' )
14
- m.additional_data['foo'].must_equal 'bar'
15
- m.additional_data['this'].must_equal 'that'
14
+ _(m.additional_data['foo']).must_equal 'bar'
15
+ _(m.additional_data['this']).must_equal 'that'
16
16
 
17
17
  m = Hitimes::Metric.new( "more-data", { 'foo' => 'bar', 'this' => 'that' } )
18
- m.additional_data['foo'].must_equal 'bar'
19
- m.additional_data['this'].must_equal 'that'
18
+ _(m.additional_data['foo']).must_equal 'bar'
19
+ _(m.additional_data['this']).must_equal 'that'
20
20
  end
21
21
 
22
22
  it "initially has no sampling times" do
23
- @metric.sampling_start_time.must_be_nil
24
- @metric.sampling_stop_time.must_be_nil
23
+ _(@metric.sampling_start_time).must_be_nil
24
+ _(@metric.sampling_stop_time).must_be_nil
25
25
  end
26
26
  end
27
27
 
@@ -16,21 +16,14 @@ describe Hitimes::MutexedStats do
16
16
  return stats
17
17
  end
18
18
 
19
- if (not defined? RUBY_ENGINE) or (RUBY_ENGINE == "ruby") then
20
- it "Hitimes::Stats is threadsafe" do
21
- stats = run_with_scissors( ::Hitimes::Stats.new, @threads, @iters )
22
- stats.count.must_equal @final_value
23
- end
24
- else
25
- it "Hitimes::Stats is not threadsafe" do
26
- stats = run_with_scissors( ::Hitimes::Stats.new, @threads, @iters )
27
- stats.count.wont_equal @final_value
28
- end
19
+ it "Hitimes::Stats is threadsafe" do
20
+ stats = run_with_scissors( ::Hitimes::Stats.new, @threads, @iters )
21
+ _(stats.count).must_equal @final_value
29
22
  end
30
23
 
31
24
  it "has a threadsafe update" do
32
25
  stats = run_with_scissors( ::Hitimes::MutexedStats.new, @threads, @iters )
33
- stats.count.must_equal @final_value
26
+ _(stats.count).must_equal @final_value
34
27
  end
35
28
 
36
29
  end
@@ -2,10 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Hitimes::Paths do
4
4
  it "can access the root dir of the project" do
5
- Hitimes::Paths.root_dir.must_equal File.expand_path( File.join( File.dirname( __FILE__ ), ".." ) ) + ::File::SEPARATOR
5
+ _(Hitimes::Paths.root_dir).must_equal File.expand_path( File.join( File.dirname( __FILE__ ), ".." ) ) + ::File::SEPARATOR
6
6
  end
7
7
 
8
8
  it "can access the lib path of the project" do
9
- Hitimes::Paths.lib_path.must_equal File.expand_path( File.join( File.dirname( __FILE__ ), "..", "lib" ) ) + ::File::SEPARATOR
9
+ _(Hitimes::Paths.lib_path).must_equal File.expand_path( File.join( File.dirname( __FILE__ ), "..", "lib" ) ) + ::File::SEPARATOR
10
10
  end
11
11
  end
@@ -9,65 +9,65 @@ describe Hitimes::Stats do
9
9
  [ 1, 2, 3].each { |i| @full_stats.update( i ) }
10
10
  end
11
11
 
12
- it "is initialized with 0 values" do
13
- @stats.count.must_equal 0
14
- @stats.min.must_equal 0.0
15
- @stats.max.must_equal 0.0
16
- @stats.sum.must_equal 0.0
17
- @stats.rate.must_equal 0.0
12
+ it "is initialized with usable values" do
13
+ _(@stats.count).must_equal 0
14
+ _(@stats.min).must_equal Float::INFINITY
15
+ _(@stats.max).must_equal(-Float::INFINITY)
16
+ _(@stats.sum).must_equal 0.0
17
+ _(@stats.rate).must_equal 0.0
18
18
  end
19
19
 
20
20
  it "calculates the mean correctly" do
21
- @full_stats.mean.must_equal 2.0
21
+ _(@full_stats.mean).must_equal 2.0
22
22
  end
23
23
 
24
24
  it "calculates the rate correctly" do
25
- @full_stats.rate.must_equal 0.5
25
+ _(@full_stats.rate).must_equal 0.5
26
26
  end
27
27
 
28
28
  it "tracks the maximum value" do
29
- @full_stats.max.must_equal 3.0
29
+ _(@full_stats.max).must_equal 3.0
30
30
  end
31
31
 
32
32
  it "tracks the minimum value" do
33
- @full_stats.min.must_equal 1.0
33
+ _(@full_stats.min).must_equal 1.0
34
34
  end
35
35
 
36
36
  it "tracks the count" do
37
- @full_stats.count.must_equal 3
37
+ _(@full_stats.count).must_equal 3
38
38
  end
39
39
 
40
40
  it "tracks the sum" do
41
- @full_stats.sum.must_equal 6.0
41
+ _(@full_stats.sum).must_equal 6.0
42
42
  end
43
43
 
44
44
  it "calculates the standard deviation" do
45
- @full_stats.stddev.must_equal 1.0
45
+ _(@full_stats.stddev).must_equal 1.0
46
46
  end
47
47
 
48
48
  it "calculates the sum of squares " do
49
- @full_stats.sumsq.must_equal 14.0
49
+ _(@full_stats.sumsq).must_equal 14.0
50
50
  end
51
51
 
52
52
  describe "#to_hash " do
53
53
  it "converts to a Hash" do
54
54
  h = @full_stats.to_hash
55
- h.size.must_equal ::Hitimes::Stats::STATS.size
56
- h.keys.sort.must_equal ::Hitimes::Stats::STATS
55
+ _(h.size).must_equal ::Hitimes::Stats::STATS.size
56
+ _(h.keys.sort).must_equal ::Hitimes::Stats::STATS
57
57
  end
58
58
 
59
59
  it "converts to a limited Hash if given arguments" do
60
60
  h = @full_stats.to_hash( "min", "max", "mean" )
61
- h.size.must_equal 3
62
- h.keys.sort.must_equal %w[ max mean min ]
61
+ _(h.size).must_equal 3
62
+ _(h.keys.sort).must_equal %w[ max mean min ]
63
63
 
64
64
  h = @full_stats.to_hash( %w[ count rate ] )
65
- h.size.must_equal 2
66
- h.keys.sort.must_equal %w[ count rate ]
65
+ _(h.size).must_equal 2
66
+ _(h.keys.sort).must_equal %w[ count rate ]
67
67
  end
68
68
 
69
69
  it "raises NoMethodError if an invalid stat is used" do
70
- lambda { @full_stats.to_hash( "wibble" ) }.must_raise( NoMethodError )
70
+ _(lambda { @full_stats.to_hash( "wibble" ) }).must_raise( NoMethodError )
71
71
  end
72
72
  end
73
73
 
@@ -75,24 +75,24 @@ describe Hitimes::Stats do
75
75
  it "converts to a json string" do
76
76
  j = @full_stats.to_json
77
77
  h = JSON.parse( j )
78
- h.size.must_equal ::Hitimes::Stats::STATS.size
79
- h.keys.sort.must_equal ::Hitimes::Stats::STATS
78
+ _(h.size).must_equal ::Hitimes::Stats::STATS.size
79
+ _(h.keys.sort).must_equal ::Hitimes::Stats::STATS
80
80
  end
81
81
 
82
82
  it "converts to a limited Hash if given arguments" do
83
83
  j = @full_stats.to_json( "min", "max", "mean" )
84
84
  h = JSON.parse( j )
85
- h.size.must_equal 3
86
- h.keys.sort.must_equal %w[ max mean min ]
85
+ _(h.size).must_equal 3
86
+ _(h.keys.sort).must_equal %w[ max mean min ]
87
87
 
88
88
  j = @full_stats.to_json( %w[ count rate ] )
89
89
  h = JSON.parse( j )
90
- h.size.must_equal 2
91
- h.keys.sort.must_equal %w[ count rate ]
90
+ _(h.size).must_equal 2
91
+ _(h.keys.sort).must_equal %w[ count rate ]
92
92
  end
93
93
 
94
94
  it "raises NoMethodError if an invalid stat is used" do
95
- lambda { @full_stats.to_json( "wibble" ) }.must_raise( NoMethodError )
95
+ _(lambda { @full_stats.to_json( "wibble" ) }).must_raise( NoMethodError )
96
96
  end
97
97
  end
98
98
  end