hitimes 1.3.1 → 3.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +1 -12
  3. data/HISTORY.md +26 -2
  4. data/Manifest.txt +5 -31
  5. data/README.md +44 -59
  6. data/hitimes.gemspec +26 -0
  7. data/lib/hitimes/initialize.rb +104 -0
  8. data/lib/hitimes/instant.rb +41 -0
  9. data/lib/hitimes/interval.rb +171 -0
  10. data/lib/hitimes/metric.rb +18 -22
  11. data/lib/hitimes/mutexed_stats.rb +3 -20
  12. data/lib/hitimes/paths.rb +16 -14
  13. data/lib/hitimes/stats.rb +119 -22
  14. data/lib/hitimes/timed_metric.rb +43 -42
  15. data/lib/hitimes/timed_value_metric.rb +43 -43
  16. data/lib/hitimes/value_metric.rb +16 -15
  17. data/lib/hitimes/version.rb +3 -1
  18. data/lib/hitimes.rb +14 -41
  19. metadata +24 -157
  20. data/Rakefile +0 -28
  21. data/examples/benchmarks.rb +0 -113
  22. data/examples/stats.rb +0 -31
  23. data/ext/hitimes/c/extconf.rb +0 -24
  24. data/ext/hitimes/c/hitimes.c +0 -37
  25. data/ext/hitimes/c/hitimes_instant_clock_gettime.c +0 -28
  26. data/ext/hitimes/c/hitimes_instant_osx.c +0 -48
  27. data/ext/hitimes/c/hitimes_instant_windows.c +0 -27
  28. data/ext/hitimes/c/hitimes_interval.c +0 -370
  29. data/ext/hitimes/c/hitimes_interval.h +0 -73
  30. data/ext/hitimes/c/hitimes_stats.c +0 -269
  31. data/ext/hitimes/c/hitimes_stats.h +0 -30
  32. data/ext/hitimes/java/src/hitimes/Hitimes.java +0 -63
  33. data/ext/hitimes/java/src/hitimes/HitimesInterval.java +0 -176
  34. data/ext/hitimes/java/src/hitimes/HitimesService.java +0 -16
  35. data/ext/hitimes/java/src/hitimes/HitimesStats.java +0 -112
  36. data/spec/hitimes_spec.rb +0 -24
  37. data/spec/interval_spec.rb +0 -136
  38. data/spec/metric_spec.rb +0 -28
  39. data/spec/mutex_stats_spec.rb +0 -36
  40. data/spec/paths_spec.rb +0 -11
  41. data/spec/spec_helper.rb +0 -11
  42. data/spec/stats_spec.rb +0 -98
  43. data/spec/timed_metric_spec.rb +0 -155
  44. data/spec/timed_value_metric_spec.rb +0 -171
  45. data/spec/value_metric_spec.rb +0 -108
  46. data/spec/version_spec.rb +0 -7
  47. data/tasks/default.rake +0 -242
  48. data/tasks/extension.rake +0 -38
  49. data/tasks/this.rb +0 -208
  50. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  #--
3
4
  # Copyright (c) 2008, 2009 Jeremy Hinegardner
@@ -13,15 +14,14 @@ module Hitimes
13
14
  # * The name of the metric
14
15
  # * The time of day the first measurement is taken
15
16
  # * The time of day the last measurement is taken
16
- # * additional data
17
+ # * additional data
17
18
  #
18
19
  # Each derived class is assumed to set the sampling_start_time and
19
20
  # sampling_stop_time appropriately.
20
- #
21
+ #
21
22
  # Metric itself should generally not be used. Only use the derived classes.
22
23
  #
23
24
  class Metric
24
-
25
25
  # the number of seconds as a float since the sampling_start_time
26
26
  attr_reader :sampling_delta
27
27
 
@@ -41,7 +41,7 @@ module Hitimes
41
41
  # +additional_data+ may be anything that follows the +to_hash+ protocol.
42
42
  # +name+ may be anything that follows the +to_s+ protocol.
43
43
  #
44
- def initialize( name, additional_data = {} )
44
+ def initialize(name, additional_data = {})
45
45
  @sampling_start_time = nil
46
46
  @sampling_start_interval = nil
47
47
  @sampling_delta = 0
@@ -51,7 +51,7 @@ module Hitimes
51
51
  end
52
52
 
53
53
  #
54
- # :call-seq:
54
+ # :call-seq:
55
55
  # metric.sampling_start_time -> Float or nil
56
56
  #
57
57
  # The time at which the first sample was taken.
@@ -60,11 +60,9 @@ module Hitimes
60
60
  # If the metric has not started measuring then the start time is nil.
61
61
  #
62
62
  def sampling_start_time
63
- if @sampling_start_interval then
64
- @sampling_start_time ||= self.utc_microseconds()
65
- else
66
- nil
67
- end
63
+ return unless @sampling_start_interval
64
+
65
+ @sampling_start_time ||= utc_microseconds
68
66
  end
69
67
 
70
68
  #
@@ -73,8 +71,8 @@ module Hitimes
73
71
  #
74
72
  # The time at which the last sample was taken
75
73
  # This is the number of microseconds since UNIX epoch UTC as a Float
76
- #
77
- # If the metric has not completely measured at least one thing then
74
+ #
75
+ # If the metric has not completely measured at least one thing then
78
76
  # stop time is nil.
79
77
  #
80
78
  # Because accessing the actual 'time of day' is an expesive operation, we
@@ -84,11 +82,9 @@ module Hitimes
84
82
  # When sampling_stop_time is called, the actual time of day is caculated.
85
83
  #
86
84
  def sampling_stop_time
87
- if @sampling_delta > 0 then
88
- (self.sampling_start_time + (@sampling_delta * 1_000_000))
89
- else
90
- nil
91
- end
85
+ return unless @sampling_delta.positive?
86
+
87
+ (sampling_start_time + (@sampling_delta * 1_000_000))
92
88
  end
93
89
 
94
90
  #
@@ -96,13 +92,13 @@ module Hitimes
96
92
  # metric.to_hash -> Hash
97
93
  # metric.to_hash
98
94
  #
99
- # Convert the metric to a Hash.
95
+ # Convert the metric to a Hash.
100
96
  #
101
97
  def to_hash
102
- { 'sampling_start_time' => self.sampling_start_time,
103
- 'sampling_stop_time' => self.sampling_stop_time,
104
- 'additional_data' => self.additional_data,
105
- 'name' => self.name }
98
+ { "sampling_start_time" => sampling_start_time,
99
+ "sampling_stop_time" => sampling_stop_time,
100
+ "additional_data" => additional_data,
101
+ "name" => name, }
106
102
  end
107
103
 
108
104
  #
@@ -1,32 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
4
  # Copyright (c) 2008, 2009 Jeremy Hinegardner
3
5
  # All rights reserved. See LICENSE and/or COPYING for details.
4
6
  #++
5
7
 
6
- require 'thread'
7
-
8
8
  module Hitimes
9
9
  #
10
10
  # MutexedStats is the start of a threadsafe Stats class. Currently, on MRI
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
-
32
-
data/lib/hitimes/paths.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
4
  # Copyright (c) 2008 Jeremy Hinegardner
3
5
  # All rights reserved. See LICENSE and/or COPYING for details.
@@ -8,34 +10,34 @@ module Hitimes
8
10
  # Access to various paths inside the project programatically
9
11
  #
10
12
  module Paths
11
- #
13
+ #
12
14
  # :call-seq:
13
15
  # Hitimes::Paths.root_dir -> String
14
16
  #
15
17
  # Returns The full expanded path of the parent directory of +lib+
16
- # going up the path from the current file. A trailing File::SEPARATOR
18
+ # going up the path from the current file. A trailing File::SEPARATOR
17
19
  # is guaranteed.
18
- #
20
+ #
19
21
  def self.root_dir
20
- @root_dir ||=(
22
+ @root_dir ||= begin
21
23
  path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR)
22
24
  lib_index = path_parts.rindex("lib")
23
25
  @root_dir = path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR
24
- )
25
- end
26
+ end
27
+ end
26
28
 
27
- #
29
+ #
28
30
  # :call-seq:
29
31
  # Hitimes::Paths.lib_path( *args ) -> String
30
32
  #
31
33
  # Returns The full expanded path of the +lib+ directory below
32
- # _root_dir_. All parameters passed in are joined onto the
33
- # result. A trailing File::SEPARATOR is guaranteed if
34
+ # _root_dir_. All parameters passed in are joined onto the
35
+ # result. A trailing File::SEPARATOR is guaranteed if
34
36
  # _args_ are *not* present.
35
- #
37
+ #
36
38
  def self.lib_path(*args)
37
- self.sub_path("lib", *args)
38
- end
39
+ sub_path("lib", *args)
40
+ end
39
41
 
40
42
  #
41
43
  # :call-seq:
@@ -45,9 +47,9 @@ module Hitimes
45
47
  # _arg_ parameters passed in are joined onto the result. A trailing
46
48
  # File::SEPARATOR is guaranteed if _args_ are *not* present.
47
49
  #
48
- def self.sub_path(sub,*args)
50
+ def self.sub_path(sub, *args)
49
51
  sp = ::File.join(root_dir, sub) + File::SEPARATOR
50
- sp = ::File.join(sp, *args) if args
52
+ ::File.join(sp, *args) if args
51
53
  end
52
54
  end
53
55
  end
data/lib/hitimes/stats.rb CHANGED
@@ -1,34 +1,132 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
4
  # Copyright (c) 2008, 2009 Jeremy Hinegardner
3
5
  # All rights reserved. See LICENSE and/or COPYING for details.
4
6
  #++
5
7
 
6
- require 'stringio'
8
+ require "stringio"
7
9
  module Hitimes
10
+ #
11
+ # The Stats class encapulsates capturing and reporting statistics. It is
12
+ # modeled after the RFuzz::Sampler class, but implemented in C. For general use
13
+ # you allocate a new Stats object, and then update it with new values. The
14
+ # Stats object will keep track of the _min_, _max_, _count_, _sum_ and _sumsq_
15
+ # and when you want you may also retrieve the _mean_, _stddev_ and _rate_.
16
+ #
17
+ # this contrived example shows getting a list of all the files in a directory
18
+ # and running stats on file sizes.
19
+ #
20
+ # s = Hitimes::Stats.new
21
+ # dir = ARGV.shift || Dir.pwd
22
+ # Dir.entries( dir ).each do |entry|
23
+ # fs = File.stat( entry )
24
+ # if fs.file? then
25
+ # s.update( fs.size )
26
+ # end
27
+ # end
28
+ #
29
+ # %w[ count min max mean sum stddev rate ].each do |m|
30
+ # puts "#{m.rjust(6)} : #{s.send( m ) }"
31
+ # end
32
+ #
8
33
  class Stats
9
34
  # A list of the available stats
10
- STATS = %w[ count max mean min rate stddev sum sumsq ]
35
+ STATS = %w[count max mean min rate stddev sum sumsq].freeze
36
+
37
+ attr_reader :min, :max, :count, :sum, :sumsq
38
+
39
+ def initialize
40
+ @mutex = Mutex.new
41
+ @min = Float::INFINITY
42
+ @max = -Float::INFINITY
43
+ @count = 0
44
+ @sum = 0.0
45
+ @sumsq = 0.0
46
+ end
11
47
 
12
- #
48
+ # call-seq:
49
+ # stat.update( val ) -> val
50
+ #
51
+ # Update the running stats with the new value.
52
+ # Return the input value.
53
+ def update(value)
54
+ @mutex.synchronize do
55
+ @min = (value < @min) ? value : @min
56
+ @max = (value > @max) ? value : @max
57
+
58
+ @count += 1
59
+ @sum += value
60
+ @sumsq += (value * value)
61
+ end
62
+
63
+ value
64
+ end
65
+
66
+ # call-seq:
67
+ # stat.mean -> Float
68
+ #
69
+ # Return the arithmetic mean of the values put into the Stats object. If no
70
+ # values have passed through the stats object then 0.0 is returned;
71
+ def mean
72
+ return 0.0 if @count.zero?
73
+
74
+ @sum / @count
75
+ end
76
+
77
+ # call-seq:
78
+ # stat.rate -> Float
79
+ #
80
+ # Return the +count+ divided by +sum+.
81
+ #
82
+ # In many cases when Stats#update( _value_ ) is called, the _value_ is a unit
83
+ # of time, typically seconds or microseconds. #rate is a convenience for those
84
+ # times. In this case, where _value_ is a unit if time, then count divided by
85
+ # sum is a useful value, i.e. +something per unit of time+.
86
+ #
87
+ # In the case where _value_ is a non-time related value, then the value
88
+ # returned by _rate_ is not really useful.
89
+ #
90
+ def rate
91
+ return 0.0 if @sum.zero?
92
+
93
+ @count / @sum
94
+ end
95
+
96
+ #
97
+ # call-seq:
98
+ # stat.stddev -> Float
99
+ #
100
+ # Return the standard deviation of all the values that have passed through the
101
+ # Stats object. The standard deviation has no meaning unless the count is > 1,
102
+ # therefore if the current _stat.count_ is < 1 then 0.0 will be returned;
103
+ #
104
+ def stddev
105
+ return 0.0 unless @count > 1
106
+
107
+ Math.sqrt((@sumsq - ((@sum * @sum) / @count)) / (@count - 1))
108
+ end
109
+
110
+ #
13
111
  # call-seq:
14
112
  # stat.to_hash -> Hash
15
113
  # stat.to_hash( %w[ count max mean ]) -> Hash
16
114
  #
17
115
  # return a hash of the stats. By default this returns a hash of all stats
18
116
  # but passing in an array of items will limit the stats returned to only
19
- # those in the Array.
117
+ # those in the Array.
20
118
  #
21
119
  # If passed in an empty array or nil to to_hash then STATS is assumed to be
22
120
  # the list of stats to return in the hash.
23
121
  #
24
- def to_hash( *args )
25
- h = {}
26
- args = [ args ].flatten
27
- args = STATS if args.empty?
28
- args.each do |meth|
29
- h[meth] = self.send( meth )
122
+ def to_hash(*args)
123
+ result = {}
124
+ fields = [args].flatten
125
+ fields = STATS if fields.empty?
126
+ fields.each do |meth|
127
+ result[meth] = send(meth)
30
128
  end
31
- return h
129
+ result
32
130
  end
33
131
 
34
132
  #
@@ -40,19 +138,18 @@ module Hitimes
40
138
  # of all the stats. If an array of items is passed in, those that match the
41
139
  # known stats will be all that is included in the json output.
42
140
  #
43
- def to_json( *args )
44
- h = to_hash( *args )
45
- a = []
46
- s = StringIO.new
141
+ def to_json(*args)
142
+ stats = to_hash(*args)
143
+ slugs = []
144
+ out = StringIO.new
47
145
 
48
- s.print "{ "
49
- h.each_pair do |k,v|
50
- a << "\"#{k}\": #{v}"
146
+ out.print "{ "
147
+ stats.each_pair do |key, val|
148
+ slugs << "\"#{key}\": #{val}"
51
149
  end
52
- s.print a.join(", ")
53
- s.print "}"
54
- return s.string
150
+ out.print slugs.join(", ")
151
+ out.print "}"
152
+ out.string
55
153
  end
56
-
57
154
  end
58
155
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
4
  # Copyright (c) 2008, 2009 Jeremy Hinegardner
3
5
  # All rights reserved. See LICENSE and/or COPYING for details.
4
6
  #++
5
7
 
6
- require 'forwardable'
8
+ require "forwardable"
7
9
  module Hitimes
8
10
  #
9
11
  # A TimedMetric holds the metrics on how long it takes to do something. For
@@ -11,11 +13,11 @@ module Hitimes
11
13
  #
12
14
  # tm = TimedMetric.new( 'my-method' )
13
15
  #
14
- # 200.times do
15
- # my_method_result = tm.measure do
16
+ # 200.times do
17
+ # my_method_result = tm.measure do
16
18
  # my_method( ... )
17
19
  # end
18
- # end
20
+ # end
19
21
  #
20
22
  # puts "#{ tm.name } operated at a rate of #{ tm.rate } calls per second"
21
23
  #
@@ -23,9 +25,9 @@ module Hitimes
23
25
  # Metric API also.
24
26
  #
25
27
  # A TimedMetric measures the execution time of an option with the Interval
26
- # class.
27
- #
28
- # A TimedMetric contains a Stats object, therefore TimedMetric has +count+, +max+,
28
+ # class.
29
+ #
30
+ # A TimedMetric contains a Stats object, therefore TimedMetric has +count+, +max+,
29
31
  # +mean+, +min+, +rate+, +stddev+, +sum+, +sumsq+ methods that delegate to that Stats
30
32
  # object for convenience.
31
33
  #
@@ -41,10 +43,10 @@ module Hitimes
41
43
  #
42
44
  # Return a TimedMetric that has been started
43
45
  #
44
- def now( name, additional_data = {} )
45
- t = TimedMetric.new( name, additional_data )
46
- t.start
47
- return t
46
+ def now(name, additional_data = {})
47
+ tm = TimedMetric.new(name, additional_data)
48
+ tm.start
49
+ tm
48
50
  end
49
51
  end
50
52
 
@@ -56,8 +58,8 @@ module Hitimes
56
58
  # Create a new TimedMetric giving it a name and additional data.
57
59
  # +additional_data+ may be anything that follows the +to_hash+ protocol
58
60
  #
59
- def initialize( name, additional_data = {} )
60
- super( name, additional_data )
61
+ def initialize(name, additional_data = {})
62
+ super(name, additional_data)
61
63
  @stats = Stats.new
62
64
  @current_interval = Interval.new
63
65
  end
@@ -66,23 +68,23 @@ module Hitimes
66
68
  # :call-seq:
67
69
  # timed_metric.running? -> true or false
68
70
  #
69
- # return whether or not the timer is currently running.
71
+ # return whether or not the timer is currently running.
70
72
  #
71
73
  def running?
72
74
  @current_interval.running?
73
75
  end
74
76
 
75
- #
77
+ #
76
78
  # :call-seq:
77
79
  # timed_metric.start -> nil
78
80
  #
79
81
  # Start the current metric, if the current metric is already started, then
80
- # this is a noop.
82
+ # this is a noop.
81
83
  #
82
84
  def start
83
- if not @current_interval.running? then
84
- @current_interval.start
85
- @sampling_start_time ||= self.utc_microseconds()
85
+ unless @current_interval.running?
86
+ @current_interval.start
87
+ @sampling_start_time ||= utc_microseconds
86
88
  @sampling_start_interval ||= Interval.now
87
89
  end
88
90
  nil
@@ -96,19 +98,19 @@ module Hitimes
96
98
  # interval. If the timer was stopped then the duration of the last Interval
97
99
  # is returned. If the timer was already stopped then false is returned and
98
100
  # no stats are updated.
99
- #
101
+ #
100
102
  def stop
101
- if @current_interval.running? then
102
- d = @current_interval.stop
103
- @stats.update( d )
103
+ if @current_interval.running?
104
+ duration = @current_interval.stop
105
+ @stats.update(duration)
104
106
  @current_interval = Interval.new
105
107
 
106
108
  # update the length of time we have been sampling
107
109
  @sampling_delta = @sampling_start_interval.duration_so_far
108
110
 
109
- return d
111
+ return duration
110
112
  end
111
- return false
113
+ false
112
114
  end
113
115
 
114
116
  #
@@ -118,7 +120,7 @@ module Hitimes
118
120
  # Measure the execution of a block and add those stats to the running stats.
119
121
  # The return value is the return value of the block
120
122
  #
121
- def measure( &block )
123
+ def measure
122
124
  return_value = nil
123
125
  begin
124
126
  start
@@ -126,7 +128,7 @@ module Hitimes
126
128
  ensure
127
129
  stop
128
130
  end
129
- return return_value
131
+ return_value
130
132
  end
131
133
 
132
134
  #
@@ -142,35 +144,34 @@ module Hitimes
142
144
  # interval, i.e. the split-time. If the timer is not running, nothing
143
145
  # happens and false is returned.
144
146
  #
145
- def split
146
- if @current_interval.running? then
147
+ def split
148
+ if @current_interval.running?
147
149
  next_interval = @current_interval.split
148
- d = @current_interval.duration
149
- @stats.update( d )
150
- @current_interval = next_interval
151
- return d
152
- end
153
- return false
150
+ duration = @current_interval.duration
151
+ @stats.update(duration)
152
+ @current_interval = next_interval
153
+ return duration
154
+ end
155
+ false
154
156
  end
155
157
 
156
158
  #
157
159
  # :call-seq:
158
160
  # metric.to_hash -> Hash
159
- #
161
+ #
160
162
  # Convert the metric to a hash
161
163
  #
162
164
  def to_hash
163
- h = super
164
- Stats::STATS.each do |s|
165
- h[s] = self.send( s )
165
+ result = super
166
+ Stats::STATS.each do |stat|
167
+ result[stat] = send(stat)
166
168
  end
167
- return h
169
+ result
168
170
  end
169
171
 
170
-
171
172
  # forward appropriate calls directly to the stats object
172
173
  extend Forwardable
173
174
  def_delegators :@stats, :count, :max, :mean, :min, :rate, :stddev, :sum, :sumsq
174
- alias :duration :sum
175
+ alias duration sum
175
176
  end
176
177
  end