stud 0.0.8 → 0.0.10

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.
@@ -3,26 +3,55 @@
3
3
  # * Compare performance of different implementations.
4
4
  # * run each implementation N times, compare runtimes (histogram, etc)
5
5
 
6
+ require "metriks"
7
+ require "stud/benchmark/rusage"
8
+
6
9
  module Stud
7
10
  module Benchmark
8
11
  def self.run(iterations=1, &block)
9
- i = 0
10
- data = []
11
- full_start = Time.now
12
- while i < iterations
13
- start = Time.now
14
- block.call
15
- duration = Time.now - start
16
- data << duration
17
- i += 1
18
- end
19
- return Results.new(data)
12
+ timer = Metriks::Timer.new
13
+ iterations.times { timer.time(&block) }
14
+ return Results.new(timer)
20
15
  end # def run
21
16
 
17
+ def self.runtimed(seconds=10, &block)
18
+ timer = Metriks::Timer.new
19
+ expiration = Time.now + seconds
20
+ timer.time(&block) while Time.now < expiration
21
+ return Results.new(timer)
22
+ end # def runtimed
23
+
24
+ def self.cputimed(seconds=10, &block)
25
+ timer = Metriks::Timer.new
26
+ expiration = Time.now + seconds
27
+ while Time.now < expiration
28
+ start = Stud::Benchmark::RUsage.get
29
+ block.call
30
+ finish = Stud::Benchmark::RUsage.get
31
+ cputime = (finish.user + finish.system) - (start.user + start.system)
32
+ timer.update(cputime)
33
+ end # while not expired
34
+ return Results.new(timer)
35
+ end # self.cpu
36
+
22
37
  class Results
23
38
  include Enumerable
24
39
  # Stolen from https://github.com/holman/spark/blob/master/spark
25
- TICKS = %w{▁ ▂ ▃ ▄ ▅ ▆ ▇ █}
40
+ # TICKS = %w{▁ ▂ ▃ ▄ ▅ ▆ ▇ █}
41
+
42
+ TICKS = ["\x1b[38;5;#{232 + 8}m_\x1b[0m"] + %w{▁ ▂ ▃ ▄ ▅ ▆ ▇ █}
43
+
44
+ #.collect do |tick|
45
+ # 256 color support, use grayscale
46
+ #1.times.collect do |shade|
47
+ # '38' is foreground
48
+ # '48' is background
49
+ # Grey colors start at 232, but let's use the brighter half.
50
+ # escape [ 38 ; 5 ; <color>
51
+ #"\x1b[38;5;#{232 + 12 + 2 * shade}m#{tick}\x1b[0m"
52
+ #end
53
+ #tick
54
+ #end.flatten
26
55
 
27
56
  def initialize(data)
28
57
  @data = data
@@ -39,86 +68,51 @@ module Stud
39
68
  end # def environment
40
69
 
41
70
  def each(&block)
42
- @data.each(&block)
71
+ @data.snapshot.each(&block)
43
72
  end # def each
44
73
 
45
- def log_distribution
46
- return distribution do |value|
47
- if value == 0
48
- 0 ... 0
49
- else
50
- tick = (Math.log2(value).floor).to_f rescue 0
51
- (2 ** tick) ... (2 ** (tick+1))
52
- end
53
- end
54
- end # def log_distribution
55
-
56
- def distribution(&range_compute)
57
- raise ArgumentError.new("Missing range computation block") if !block_given?
74
+ def min
75
+ return @data.min
76
+ end
58
77
 
59
- max = @data.max
60
- dist = Hash.new { |h,k| h[k] = 0 }
61
- each do |value|
62
- range = range_compute.call(value)
63
- dist[range] += 1
64
- end
65
- return dist
66
- end # def distribution
78
+ def max
79
+ return @data.max
80
+ end
67
81
 
68
82
  def mean
69
- if @mean.nil?
70
- total = Float(@data.count)
71
- @mean = sum / total
72
- end
73
- return @mean
83
+ return @data.mean
74
84
  end # def mean
75
85
 
76
86
  def stddev
77
- # sum of square deviations of mean divided by total values
78
- return Math.sqrt(inject(0) { |s, v| s + (v - mean) ** 2 } / (@data.count - 1))
87
+ # work around (Timer#stddev reports the variance)
88
+ # https://github.com/eric/metriks/pull/29
89
+ return @data.stddev ** 0.5
79
90
  end # def stddev
80
91
 
81
92
  def sum
82
- if @sum.nil?
83
- @sum = inject(0) { |s,v| s + v }
84
- end
85
- return @sum
93
+ return @data.instance_eval { @histogram.sum }
86
94
  end # def sum
87
95
 
88
- def to_s
89
- return "#{environment}: avg: #{mean} stddev: #{stddev}"
90
- end # def to_s
91
-
92
96
  def pretty_print
93
- min = @data.min
94
- max = @data.max
95
- zmax = Float(max - min) # "zero" at the 'min' value, offset the max.
96
- incr = 0.1 # 10% increments
97
- #dist = distribution do |value|
98
- #percent = (value - min) / zmax
99
- #if percent == 1
100
- #(1 - incr ... 1.0)
101
- #else
102
- #start = ((percent * 10).floor / 10.0)
103
- #start ... (start + incr)
104
- #end
105
- #end
106
- dist = log_distribution
97
+ puts self
98
+ end # def pretty_print
107
99
 
108
- total = dist.inject(0) { |sum, (step, count)| sum + count }
109
- sorted = dist.sort { |a,b| a.first.begin <=> b.first.begin }
110
- puts sorted.collect { |lower_bound, count|
111
- #puts lower_bound
112
- percent = (count / Float(total))
113
- "%40s: %s" % [lower_bound, (TICKS.last * (50 * percent).ceil)]
114
- }.join("\n")
100
+ def to_s(scale=min .. max, ticks=10)
101
+ snapshot = @data.snapshot
102
+ values = snapshot.instance_eval { @values }
103
+ scale_distance = scale.end - scale.begin
104
+ tick = scale_distance / ticks
105
+ dist = ticks.to_i.times.collect do |i|
106
+ range = (scale.begin + tick * i) ... (scale.begin + tick * (i+1))
107
+ hits = values.select { |v| range.include?(v) }.count
108
+ percent = hits / values.size.to_f
109
+ next TICKS[(TICKS.count * percent).ceil] || TICKS.last
110
+ end
115
111
 
116
- end # def pretty_print
112
+ return sprintf("%20s %s (%.4f ... %.4f, mean: %0.4f, stddev: %0.4f)",
113
+ environment, dist.join(""), scale.begin, scale.end,
114
+ mean, stddev)
115
+ end # def to_s
117
116
  end # class Stud::Benchmark::Result
118
117
  end # module Benchmark
119
118
  end # module Stud
120
-
121
- #require "thread"
122
- #mutex = Mutex.new
123
- #results = Stud::Benchmark.run(20) { mutex.synchronize { rand; rand; rand } }
124
- #results.pretty_print
@@ -0,0 +1,54 @@
1
+ require "ffi"
2
+
3
+ module Stud
4
+ module Benchmark
5
+ module LibC
6
+ extend FFI::Library
7
+ ffi_lib "libc.so.6"
8
+
9
+ attach_function :getrusage, [:int, :pointer], :int
10
+ end
11
+
12
+ class TimeVal < FFI::Struct
13
+ layout :tv_sec, :long,
14
+ :tv_usec, :int32
15
+
16
+ def to_f
17
+ return self[:tv_sec] + (self[:tv_usec] / 1_000_000.0)
18
+ end
19
+ end
20
+
21
+ class RUsage < FFI::Struct
22
+ layout :utime, TimeVal,
23
+ :stime, TimeVal,
24
+ :maxrss, :long,
25
+ :ixrss, :long,
26
+ :idrss, :long,
27
+ :isrss, :long,
28
+ :minflt, :long,
29
+ :majflt, :long,
30
+ :nswap, :long,
31
+ :inblock, :long,
32
+ :oublock, :long,
33
+ :msgsnd, :long,
34
+ :msgrcv, :long,
35
+ :nsignals, :long,
36
+ :nvcsw, :long,
37
+ :nivcsw, :long
38
+
39
+ def self.get
40
+ usage = RUsage.new
41
+ LibC.getrusage(0, usage)
42
+ return usage
43
+ end
44
+
45
+ def user
46
+ return self[:utime].to_f
47
+ end
48
+
49
+ def system
50
+ return self[:stime].to_f
51
+ end
52
+ end # class RUsage
53
+ end # module Benchmark
54
+ end # module Stud
data/lib/stud/interval.rb CHANGED
@@ -5,7 +5,7 @@ module Stud
5
5
  #
6
6
  # The execution patterns of this method should be that
7
7
  # the start time of 'block.call' should always be at time T*interval
8
- def interval(time, &block)
8
+ def self.interval(time, &block)
9
9
  start = Time.now
10
10
  while true
11
11
  block.call
@@ -20,4 +20,8 @@ module Stud
20
20
  end
21
21
  end # loop forever
22
22
  end # def interval
23
+
24
+ def interval(time, &block)
25
+ return Stud.interval(time, &block)
26
+ end # def interval
23
27
  end # module Stud
data/lib/stud/trap.rb CHANGED
@@ -19,7 +19,7 @@ module Stud
19
19
  end
20
20
 
21
21
  def self.simulate_signal(signal)
22
- puts "Simulate: #{signal}"
22
+ puts "Simulate: #{signal} w/ #{@traps[signal].count} callbacks"
23
23
  @traps[signal].each(&:call)
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,89 +1,130 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
5
- prerelease:
4
+ version: 0.0.10
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jordan Sissel
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-06 00:00:00.000000000 Z
12
+ date: 2013-01-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: rspec
16
- requirement: !ruby/object:Gem::Requirement
15
+ name: metriks
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: !binary |-
21
+ MA==
17
22
  none: false
23
+ requirement: !ruby/object:Gem::Requirement
18
24
  requirements:
19
- - - ! '>='
25
+ - - ">="
20
26
  - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :development
27
+ version: !binary |-
28
+ MA==
29
+ none: false
23
30
  prerelease: false
31
+ type: :runtime
32
+ - !ruby/object:Gem::Dependency
33
+ name: ffi
24
34
  version_requirements: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: !binary |-
39
+ MA==
25
40
  none: false
41
+ requirement: !ruby/object:Gem::Requirement
26
42
  requirements:
27
- - - ! '>='
43
+ - - ">="
28
44
  - !ruby/object:Gem::Version
29
- version: '0'
45
+ version: !binary |-
46
+ MA==
47
+ none: false
48
+ prerelease: false
49
+ type: :runtime
30
50
  - !ruby/object:Gem::Dependency
31
- name: insist
32
- requirement: !ruby/object:Gem::Requirement
51
+ name: rspec
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: !binary |-
57
+ MA==
33
58
  none: false
59
+ requirement: !ruby/object:Gem::Requirement
34
60
  requirements:
35
- - - ! '>='
61
+ - - ">="
36
62
  - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :development
63
+ version: !binary |-
64
+ MA==
65
+ none: false
39
66
  prerelease: false
67
+ type: :development
68
+ - !ruby/object:Gem::Dependency
69
+ name: insist
40
70
  version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: !binary |-
75
+ MA==
41
76
  none: false
77
+ requirement: !ruby/object:Gem::Requirement
42
78
  requirements:
43
- - - ! '>='
79
+ - - ">="
44
80
  - !ruby/object:Gem::Version
45
- version: '0'
46
- description: small reusable bits of code I'm tired of writing over and over. A library
47
- form of my software-patterns github repo.
81
+ version: !binary |-
82
+ MA==
83
+ none: false
84
+ prerelease: false
85
+ type: :development
86
+ description: small reusable bits of code I'm tired of writing over and over. A library form of my software-patterns github repo.
48
87
  email: jls@semicomplete.com
49
88
  executables: []
50
89
  extensions: []
51
90
  extra_rdoc_files: []
52
91
  files:
92
+ - lib/stud/task.rb
93
+ - lib/stud/benchmark.rb
94
+ - lib/stud/pool.rb
53
95
  - lib/stud/try.rb
54
96
  - lib/stud/interval.rb
55
- - lib/stud/benchmark.rb
56
- - lib/stud/task.rb
57
97
  - lib/stud/trap.rb
58
98
  - lib/stud/secret.rb
59
- - lib/stud/pool.rb
99
+ - lib/stud/benchmark/rusage.rb
60
100
  - LICENSE
61
101
  - CHANGELIST
62
102
  - README.md
63
103
  homepage: https://github.com/jordansissel/ruby-stud
64
104
  licenses: []
65
- post_install_message:
105
+ post_install_message:
66
106
  rdoc_options: []
67
107
  require_paths:
68
108
  - lib
69
109
  - lib
70
110
  required_ruby_version: !ruby/object:Gem::Requirement
71
- none: false
72
111
  requirements:
73
- - - ! '>='
112
+ - - ">="
74
113
  - !ruby/object:Gem::Version
75
- version: '0'
76
- required_rubygems_version: !ruby/object:Gem::Requirement
114
+ version: !binary |-
115
+ MA==
77
116
  none: false
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
118
  requirements:
79
- - - ! '>='
119
+ - - ">="
80
120
  - !ruby/object:Gem::Version
81
- version: '0'
121
+ version: !binary |-
122
+ MA==
123
+ none: false
82
124
  requirements: []
83
- rubyforge_project:
125
+ rubyforge_project:
84
126
  rubygems_version: 1.8.24
85
- signing_key:
127
+ signing_key:
86
128
  specification_version: 3
87
129
  summary: stud - common code techniques
88
130
  test_files: []
89
- has_rdoc: