hitimes 1.3.0-x64-mingw32
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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +57 -0
- data/HISTORY.md +124 -0
- data/LICENSE +16 -0
- data/Manifest.txt +44 -0
- data/README.md +200 -0
- data/Rakefile +28 -0
- data/examples/benchmarks.rb +113 -0
- data/examples/stats.rb +31 -0
- data/ext/hitimes/c/extconf.rb +24 -0
- data/ext/hitimes/c/hitimes.c +37 -0
- data/ext/hitimes/c/hitimes_instant_clock_gettime.c +28 -0
- data/ext/hitimes/c/hitimes_instant_osx.c +45 -0
- data/ext/hitimes/c/hitimes_instant_windows.c +27 -0
- data/ext/hitimes/c/hitimes_interval.c +370 -0
- data/ext/hitimes/c/hitimes_interval.h +73 -0
- data/ext/hitimes/c/hitimes_stats.c +269 -0
- data/ext/hitimes/c/hitimes_stats.h +30 -0
- data/ext/hitimes/java/src/hitimes/Hitimes.java +66 -0
- data/ext/hitimes/java/src/hitimes/HitimesInterval.java +176 -0
- data/ext/hitimes/java/src/hitimes/HitimesService.java +16 -0
- data/ext/hitimes/java/src/hitimes/HitimesStats.java +112 -0
- data/lib/hitimes.rb +66 -0
- data/lib/hitimes/2.0/hitimes.so +0 -0
- data/lib/hitimes/2.1/hitimes.so +0 -0
- data/lib/hitimes/2.2/hitimes.so +0 -0
- data/lib/hitimes/2.3/hitimes.so +0 -0
- data/lib/hitimes/2.4/hitimes.so +0 -0
- data/lib/hitimes/2.5/hitimes.so +0 -0
- data/lib/hitimes/metric.rb +118 -0
- data/lib/hitimes/mutexed_stats.rb +32 -0
- data/lib/hitimes/paths.rb +53 -0
- data/lib/hitimes/stats.rb +58 -0
- data/lib/hitimes/timed_metric.rb +176 -0
- data/lib/hitimes/timed_value_metric.rb +233 -0
- data/lib/hitimes/value_metric.rb +71 -0
- data/lib/hitimes/version.rb +8 -0
- data/spec/hitimes_spec.rb +24 -0
- data/spec/interval_spec.rb +136 -0
- data/spec/metric_spec.rb +28 -0
- data/spec/mutex_stats_spec.rb +36 -0
- data/spec/paths_spec.rb +11 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/stats_spec.rb +98 -0
- data/spec/timed_metric_spec.rb +155 -0
- data/spec/timed_value_metric_spec.rb +171 -0
- data/spec/value_metric_spec.rb +108 -0
- data/spec/version_spec.rb +7 -0
- data/tasks/default.rake +242 -0
- data/tasks/extension.rake +38 -0
- data/tasks/this.rb +208 -0
- metadata +216 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
package hitimes;
|
2
|
+
|
3
|
+
import java.io.IOException;
|
4
|
+
|
5
|
+
import org.jruby.Ruby;
|
6
|
+
|
7
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
8
|
+
|
9
|
+
public class HitimesService implements BasicLibraryService {
|
10
|
+
public boolean basicLoad( final Ruby runtime ) throws IOException {
|
11
|
+
Hitimes.createHitimesModule( runtime );
|
12
|
+
return true;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
package hitimes;
|
2
|
+
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyClass;
|
5
|
+
import org.jruby.RubyObject;
|
6
|
+
|
7
|
+
import org.jruby.RubyNumeric;
|
8
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
9
|
+
import org.jruby.runtime.ObjectAllocator;
|
10
|
+
|
11
|
+
import org.jruby.anno.JRubyMethod;
|
12
|
+
import org.jruby.anno.JRubyClass;
|
13
|
+
|
14
|
+
@JRubyClass( name = "Hitimes::Stats" )
|
15
|
+
public class HitimesStats extends RubyObject {
|
16
|
+
|
17
|
+
private double min = 0.0;
|
18
|
+
private double max = 0.0;
|
19
|
+
private double sum = 0.0;
|
20
|
+
private double sumsq = 0.0;
|
21
|
+
private long count = 0;
|
22
|
+
|
23
|
+
public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
24
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
25
|
+
return new HitimesStats( runtime, klass );
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
public HitimesStats( Ruby runtime, RubyClass klass ) {
|
30
|
+
super( runtime, klass );
|
31
|
+
}
|
32
|
+
|
33
|
+
@JRubyMethod( name = "update", required = 1, argTypes = RubyNumeric.class )
|
34
|
+
public IRubyObject update( IRubyObject val ) {
|
35
|
+
double v = RubyNumeric.num2dbl( val );
|
36
|
+
|
37
|
+
if ( 0 == this.count ) {
|
38
|
+
this.min = this.max = v;
|
39
|
+
} else {
|
40
|
+
this.min = ( v < this.min ) ? v : this.min;
|
41
|
+
this.max = ( v > this.max ) ? v : this.max;
|
42
|
+
}
|
43
|
+
|
44
|
+
this.count += 1;
|
45
|
+
this.sum += v;
|
46
|
+
this.sumsq += (v * v);
|
47
|
+
|
48
|
+
return val;
|
49
|
+
}
|
50
|
+
|
51
|
+
@JRubyMethod( name = "mean" )
|
52
|
+
public IRubyObject mean() {
|
53
|
+
double mean = 0.0;
|
54
|
+
|
55
|
+
if ( this.count > 0 ) {
|
56
|
+
mean = this.sum / this.count;
|
57
|
+
}
|
58
|
+
|
59
|
+
return getRuntime().newFloat( mean );
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
@JRubyMethod( name = "rate" )
|
64
|
+
public IRubyObject rate() {
|
65
|
+
double rate = 0.0;
|
66
|
+
|
67
|
+
if ( this.sum > 0.0 ) {
|
68
|
+
rate = this.count / this.sum ;
|
69
|
+
}
|
70
|
+
|
71
|
+
return getRuntime().newFloat( rate );
|
72
|
+
}
|
73
|
+
|
74
|
+
@JRubyMethod( name = "stddev" )
|
75
|
+
public IRubyObject stddev() {
|
76
|
+
double stddev = 0.0;
|
77
|
+
|
78
|
+
if ( this.count > 1 ) {
|
79
|
+
double sq_sum = this.sum * this.sum;
|
80
|
+
stddev = Math.sqrt( ( this.sumsq - ( sq_sum / this.count ) ) / ( this.count - 1 ) );
|
81
|
+
}
|
82
|
+
return getRuntime().newFloat( stddev );
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
@JRubyMethod( name = "min" )
|
87
|
+
public IRubyObject min() {
|
88
|
+
return getRuntime().newFloat( this.min );
|
89
|
+
}
|
90
|
+
|
91
|
+
@JRubyMethod( name = "max" )
|
92
|
+
public IRubyObject max() {
|
93
|
+
return getRuntime().newFloat( this.max );
|
94
|
+
}
|
95
|
+
|
96
|
+
@JRubyMethod( name = "sum" )
|
97
|
+
public IRubyObject sum() {
|
98
|
+
return getRuntime().newFloat( this.sum );
|
99
|
+
}
|
100
|
+
|
101
|
+
@JRubyMethod( name = "sumsq" )
|
102
|
+
public IRubyObject sumsq() {
|
103
|
+
return getRuntime().newFloat( this.sumsq );
|
104
|
+
}
|
105
|
+
|
106
|
+
@JRubyMethod( name = "count" )
|
107
|
+
public IRubyObject count() {
|
108
|
+
return getRuntime().newFixnum( this.count );
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
|
data/lib/hitimes.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
#
|
7
|
+
# The top level module containing the contents of the hitimes library
|
8
|
+
#
|
9
|
+
# use the library with:
|
10
|
+
#
|
11
|
+
# require 'hitimes'
|
12
|
+
#
|
13
|
+
module Hitimes
|
14
|
+
#
|
15
|
+
# Base class of all errors in Hitimes
|
16
|
+
#
|
17
|
+
class Error < ::StandardError; end
|
18
|
+
|
19
|
+
# Hitimes.measure { } -> Float
|
20
|
+
#
|
21
|
+
# Times the execution of the block, returning the number of seconds it took
|
22
|
+
def self.measure(&block)
|
23
|
+
Hitimes::Interval.measure(&block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
require 'hitimes/paths'
|
27
|
+
require 'hitimes/version'
|
28
|
+
|
29
|
+
# Load the binary extension, try loading one for the specific version of ruby
|
30
|
+
# and if that fails, then fall back to one in the top of the library.
|
31
|
+
# this is the method recommended by rake-compiler
|
32
|
+
|
33
|
+
attempts = [
|
34
|
+
"hitimes/#{RUBY_VERSION.sub(/\.\d+$/,'')}/hitimes",
|
35
|
+
"hitimes/hitimes"
|
36
|
+
]
|
37
|
+
loaded = false
|
38
|
+
|
39
|
+
path_exceptions = []
|
40
|
+
attempts.each do |path|
|
41
|
+
begin
|
42
|
+
require path
|
43
|
+
loaded = true
|
44
|
+
break
|
45
|
+
rescue LoadError => load_error
|
46
|
+
full_path = File.expand_path(path)
|
47
|
+
path_exceptions << [ full_path, load_error.message ]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if !loaded then
|
52
|
+
msg = ["Unable to find binary extension, was hitimes installed correctly? The following paths were tried."]
|
53
|
+
path_exceptions.each do |path, message|
|
54
|
+
msg << "#{path} : #{message}"
|
55
|
+
end
|
56
|
+
raise LoadError, msg.join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
require 'hitimes/stats'
|
60
|
+
require 'hitimes/mutexed_stats'
|
61
|
+
|
62
|
+
require 'hitimes/metric'
|
63
|
+
require 'hitimes/value_metric'
|
64
|
+
require 'hitimes/timed_metric'
|
65
|
+
require 'hitimes/timed_value_metric'
|
66
|
+
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2008, 2009 Jeremy Hinegardner
|
4
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
module Hitimes
|
8
|
+
#
|
9
|
+
# Metric hold the common meta information for all derived metric classes
|
10
|
+
#
|
11
|
+
# All metrics hold the meta information of:
|
12
|
+
#
|
13
|
+
# * The name of the metric
|
14
|
+
# * The time of day the first measurement is taken
|
15
|
+
# * The time of day the last measurement is taken
|
16
|
+
# * additional data
|
17
|
+
#
|
18
|
+
# Each derived class is assumed to set the sampling_start_time and
|
19
|
+
# sampling_stop_time appropriately.
|
20
|
+
#
|
21
|
+
# Metric itself should generally not be used. Only use the derived classes.
|
22
|
+
#
|
23
|
+
class Metric
|
24
|
+
|
25
|
+
# the number of seconds as a float since the sampling_start_time
|
26
|
+
attr_reader :sampling_delta
|
27
|
+
|
28
|
+
# An additional hash of data to associate with the metric
|
29
|
+
attr_reader :additional_data
|
30
|
+
|
31
|
+
# The 'name' to associate with the metric
|
32
|
+
attr_reader :name
|
33
|
+
|
34
|
+
#
|
35
|
+
# :call-seq:
|
36
|
+
# Metric.new( 'my_metric' ) -> Metric
|
37
|
+
# Metric.new( 'my_metric', 'foo' => 'bar', 'this' => 42 ) -> Metric
|
38
|
+
#
|
39
|
+
# Create a new ValueMetric giving it a name and additional data.
|
40
|
+
#
|
41
|
+
# +additional_data+ may be anything that follows the +to_hash+ protocol.
|
42
|
+
# +name+ may be anything that follows the +to_s+ protocol.
|
43
|
+
#
|
44
|
+
def initialize( name, additional_data = {} )
|
45
|
+
@sampling_start_time = nil
|
46
|
+
@sampling_start_interval = nil
|
47
|
+
@sampling_delta = 0
|
48
|
+
|
49
|
+
@name = name.to_s
|
50
|
+
@additional_data = additional_data.to_hash
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# :call-seq:
|
55
|
+
# metric.sampling_start_time -> Float or nil
|
56
|
+
#
|
57
|
+
# The time at which the first sample was taken.
|
58
|
+
# This is the number of microseconds since UNIX epoch UTC as a Float
|
59
|
+
#
|
60
|
+
# If the metric has not started measuring then the start time is nil.
|
61
|
+
#
|
62
|
+
def sampling_start_time
|
63
|
+
if @sampling_start_interval then
|
64
|
+
@sampling_start_time ||= self.utc_microseconds()
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# :call-seq:
|
72
|
+
# metric.sampling_stop_time -> Float or nil
|
73
|
+
#
|
74
|
+
# The time at which the last sample was taken
|
75
|
+
# 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
|
78
|
+
# stop time is nil.
|
79
|
+
#
|
80
|
+
# Because accessing the actual 'time of day' is an expesive operation, we
|
81
|
+
# only get the time of day at the beginning of the first measurement and we
|
82
|
+
# keep track of the offset from that point in @sampling_delta.
|
83
|
+
#
|
84
|
+
# When sampling_stop_time is called, the actual time of day is caculated.
|
85
|
+
#
|
86
|
+
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
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# :call-seq:
|
96
|
+
# metric.to_hash -> Hash
|
97
|
+
# metric.to_hash
|
98
|
+
#
|
99
|
+
# Convert the metric to a Hash.
|
100
|
+
#
|
101
|
+
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 }
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# :call-seq:
|
110
|
+
# metric.utc_microseconds -> Float
|
111
|
+
#
|
112
|
+
# The current time in microseconds from the UNIX Epoch in the UTC
|
113
|
+
#
|
114
|
+
def utc_microseconds
|
115
|
+
Time.now.gmtime.to_f * 1_000_000
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008, 2009 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
module Hitimes
|
9
|
+
#
|
10
|
+
# MutexedStats is the start of a threadsafe Stats class. Currently, on MRI
|
11
|
+
# Ruby the Stats object is already threadsafe, so there is no need to use
|
12
|
+
# MutexedStats.
|
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
|
30
|
+
end
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
#
|
6
|
+
module Hitimes
|
7
|
+
#
|
8
|
+
# Access to various paths inside the project programatically
|
9
|
+
#
|
10
|
+
module Paths
|
11
|
+
#
|
12
|
+
# :call-seq:
|
13
|
+
# Hitimes::Paths.root_dir -> String
|
14
|
+
#
|
15
|
+
# Returns The full expanded path of the parent directory of +lib+
|
16
|
+
# going up the path from the current file. A trailing File::SEPARATOR
|
17
|
+
# is guaranteed.
|
18
|
+
#
|
19
|
+
def self.root_dir
|
20
|
+
@root_dir ||=(
|
21
|
+
path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR)
|
22
|
+
lib_index = path_parts.rindex("lib")
|
23
|
+
@root_dir = path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# :call-seq:
|
29
|
+
# Hitimes::Paths.lib_path( *args ) -> String
|
30
|
+
#
|
31
|
+
# 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
|
+
# _args_ are *not* present.
|
35
|
+
#
|
36
|
+
def self.lib_path(*args)
|
37
|
+
self.sub_path("lib", *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# :call-seq:
|
42
|
+
# Hitimes::Paths.sub_path( sub, *args ) -> String
|
43
|
+
#
|
44
|
+
# Returns the full expanded path of the +sub+ directory below _root_dir. All
|
45
|
+
# _arg_ parameters passed in are joined onto the result. A trailing
|
46
|
+
# File::SEPARATOR is guaranteed if _args_ are *not* present.
|
47
|
+
#
|
48
|
+
def self.sub_path(sub,*args)
|
49
|
+
sp = ::File.join(root_dir, sub) + File::SEPARATOR
|
50
|
+
sp = ::File.join(sp, *args) if args
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008, 2009 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'stringio'
|
7
|
+
module Hitimes
|
8
|
+
class Stats
|
9
|
+
# A list of the available stats
|
10
|
+
STATS = %w[ count max mean min rate stddev sum sumsq ]
|
11
|
+
|
12
|
+
#
|
13
|
+
# call-seq:
|
14
|
+
# stat.to_hash -> Hash
|
15
|
+
# stat.to_hash( %w[ count max mean ]) -> Hash
|
16
|
+
#
|
17
|
+
# return a hash of the stats. By default this returns a hash of all stats
|
18
|
+
# but passing in an array of items will limit the stats returned to only
|
19
|
+
# those in the Array.
|
20
|
+
#
|
21
|
+
# If passed in an empty array or nil to to_hash then STATS is assumed to be
|
22
|
+
# the list of stats to return in the hash.
|
23
|
+
#
|
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 )
|
30
|
+
end
|
31
|
+
return h
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# call-seq:
|
36
|
+
# stat.to_json -> String
|
37
|
+
# stat.to_json( *args ) -> String
|
38
|
+
#
|
39
|
+
# return a json string of the stats. By default this returns a json string
|
40
|
+
# of all the stats. If an array of items is passed in, those that match the
|
41
|
+
# known stats will be all that is included in the json output.
|
42
|
+
#
|
43
|
+
def to_json( *args )
|
44
|
+
h = to_hash( *args )
|
45
|
+
a = []
|
46
|
+
s = StringIO.new
|
47
|
+
|
48
|
+
s.print "{ "
|
49
|
+
h.each_pair do |k,v|
|
50
|
+
a << "\"#{k}\": #{v}"
|
51
|
+
end
|
52
|
+
s.print a.join(", ")
|
53
|
+
s.print "}"
|
54
|
+
return s.string
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|