benelux 0.4.2 → 0.5.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.
data/CHANGES.txt CHANGED
@@ -1,6 +1,21 @@
1
1
  BENELUX, CHANGES
2
2
 
3
3
 
4
+ #### 0.4.4 (2009-10-20) ###############################
5
+
6
+ * CHANGE: Removed reporter
7
+ * CHANGE: Take advantage of Ruby's accidentally thread-safe
8
+ Arrays and use global timeline
9
+ * CHANGE: Removed Timeline#counts
10
+
11
+
12
+ #### 0.4.3 (2009-10-08) ###############################
13
+
14
+ * FIXED: Initial reporting loop waits 1 second for first threads
15
+ * CHANGE: Huge performance improvement (no tag matching when merging stats)
16
+ * CHANGED: Cleaned Reporter
17
+
18
+
4
19
  #### 0.4.2 (2009-10-06) ###############################
5
20
 
6
21
  * ADDED: JRuby support
data/benelux.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "benelux"
3
3
  s.rubyforge_project = 'benelux'
4
- s.version = "0.4.2"
4
+ s.version = "0.5.0"
5
5
  s.summary = "Benelux: Little freakin' timers for your Ruby codes"
6
6
  s.description = s.summary
7
7
  s.author = "Delano Mandelbaum"
@@ -46,6 +46,7 @@
46
46
  tryouts/30_timeline_tryouts.rb
47
47
  tryouts/proofs/alias_performance.rb
48
48
  tryouts/proofs/array_performance.rb
49
+ tryouts/proofs/thread_array.rb
49
50
  tryouts/proofs/timer_threading.rb
50
51
  )
51
52
 
@@ -95,16 +95,16 @@ module Benelux
95
95
  def #{@meth}(*args, &block)
96
96
  call_id = "" << self.object_id.abs.to_s << args.object_id.abs.to_s
97
97
  Benelux.current_track :global unless Benelux.known_thread?
98
- mark_a = Thread.current.timeline.add_mark :'#{@meth}_a'
98
+ mark_a = Benelux.thread_timeline.add_mark :'#{@meth}_a'
99
99
  mark_a.add_tag :call_id => call_id
100
100
  tags = mark_a.tags
101
101
  ret = #{@aliaz}(*args, &block)
102
102
  rescue => ex # We do this so we can use
103
103
  raise ex # ex in the ensure block.
104
104
  ensure
105
- mark_z = Thread.current.timeline.add_mark :'#{@meth}_z'
105
+ mark_z = Benelux.thread_timeline.add_mark :'#{@meth}_z'
106
106
  mark_z.tags = tags # In case tags were added between these marks
107
- range = Thread.current.timeline.add_range :'#{@meth}', mark_a, mark_z
107
+ range = Benelux.thread_timeline.add_range :'#{@meth}', mark_a, mark_z
108
108
  range.exception = ex if defined?(ex) && !ex.nil?
109
109
  end
110
110
  }
@@ -9,46 +9,46 @@ module Benelux
9
9
  @abort, @running = false, false
10
10
  @tmerge = Benelux::Stats::Calculator.new
11
11
  add_threads *threads
12
+ @tbd = []
13
+ @thread = create_reporting_thread
14
+ end
15
+ def create_reporting_thread(priority=-3)
16
+ t = Thread.new do
17
+ 5.times { # Give the app 1 second to generate threads
18
+ break if @start && !@thwait.empty?
19
+ sleep 0.2
20
+ }
21
+
22
+ run_loop
23
+ end
24
+ t.priority = priority
25
+ t
12
26
  end
13
27
  def add_threads(*threads)
14
28
  threads.each do |thread|
15
- #raise BadRecursion, "Cannot report on self" if thread == Thread.current
29
+ raise BadRecursion, "Cannot report on self" if thread == @thread
16
30
  next if thread == Thread.main
17
31
  @thwait.join_nowait thread
18
32
  end
19
- return if running?
20
- @@mutex.synchronize do
21
- start
22
- end
33
+ @start = true
23
34
  end
24
35
  alias_method :add_thread, :add_threads
25
36
  def running_threads?
26
37
  # Any status that is not nil or false is running
27
- !@thwait.threads.select { |t| t.status }.empty?
28
- end
29
-
30
- def start
31
- return if running?
32
- @running = true
33
- @thread = Thread.new do
34
- while @thwait.empty?
35
- sleep 0.5 # Give the app time to generate threads
36
- end
37
- @tbd = []
38
- run_loop
39
- end
40
- @thread.priority = -3
38
+ !@thwait.threads.select { |t| !t.nil? && t.status }.empty?
41
39
  end
40
+
42
41
  def run_loop
43
42
  loop do
44
43
  break if @abort
45
44
  process(@tbd)
46
45
  if @thwait.empty?
47
- sleep 0.001
46
+ sleep 0.01 # prevent mad thrashing.
47
+ # If there are no threads running we can stop
48
+ # because there are none waiting in the queue.
48
49
  running_threads? ? next : break
49
50
  end
50
51
  t = @thwait.next_wait
51
- #p [:reporter_queue, '^', t.track_name, @thread.priority]
52
52
  @tbd << t.timeline
53
53
  end
54
54
  end
@@ -64,12 +64,16 @@ module Benelux
64
64
  end
65
65
  # We don't add the main thread to the wait group
66
66
  # so we need to manually force processing for
67
- # that thread. The reason: we
67
+ # that thread.
68
68
  def force_update
69
69
  @abort = false
70
70
  @tbd << Thread.current.timeline
71
71
  run_loop
72
72
  end
73
+ # Call this once the active threads have stopped. It
74
+ # increases the priority of the processing thread,
75
+ # waits for it to finish and then calls force_update
76
+ # to get the main threads stats into the timeline.
73
77
  def wait
74
78
  if @thread && Thread.current == Thread.main
75
79
  @abort = true
data/lib/benelux/stats.rb CHANGED
@@ -15,6 +15,9 @@ module Benelux
15
15
  g.name = name
16
16
  g
17
17
  end
18
+ def size
19
+ @names.size
20
+ end
18
21
  # Each group
19
22
  def each(&blk)
20
23
  @names.each { |name| blk.call(group(name)) }
@@ -43,13 +46,12 @@ module Benelux
43
46
  if !other.is_a?(Benelux::Stats)
44
47
  raise TypeError, "can't convert #{other.class} into Stats"
45
48
  end
49
+ s = self.clone
46
50
  other.names.each do |name|
47
- add_group name
48
- a = self.send(name)
49
- a += other.send(name)
50
- a
51
+ s.add_group name
52
+ s.group(name) << other.group(name)
51
53
  end
52
- self
54
+ s
53
55
  end
54
56
 
55
57
  class Group < Array
@@ -61,28 +63,35 @@ module Benelux
61
63
  unless @name == other.name
62
64
  raise BeneluxError, "Cannot add #{other.name} to #{@name}"
63
65
  end
64
- other.each do |newcalc|
65
- # Merge calculator with a like calculator in another group.
66
- calcs = self.select { |calc| calc.tags == newcalc.tags }
67
- # This should only ever contain one b/c we should
68
- # not have several calculators with the same tags.
69
- calcs.each do |calc|
70
- calc += newcalc
71
- end
72
- self << newcalc
73
- end
66
+ g = Group.new self
67
+ g.name = @name
68
+ g << other
69
+ g
70
+ end
71
+
72
+ def <<(other)
73
+ self.push *other
74
74
  self
75
75
  end
76
76
 
77
+ def merge(*tags)
78
+ #tags = Selectable.normalize tags
79
+ mc = Calculator.new
80
+ mc.init_tags!
81
+ all = tags.empty? ? self : self.filter(tags)
82
+ all.each { |calc|
83
+ mc.merge! calc
84
+ mc.add_tags_quick calc.tags
85
+ }
86
+ mc
87
+ end
88
+
77
89
  def sample(s, tags={})
78
- raise BeneluxError, "tags must be a Hash" unless tags.kind_of?(Hash)
79
- calcs = self.select { |c| c.tags == tags }
80
- if calcs.empty?
81
- (c = Calculator.new).add_tags tags
82
- self << c
83
- calcs = [c]
84
- end
85
- calcs.each { |c| c.sample(s) }
90
+ raise BeneluxError, "tags must be a Hash" unless Hash === tags
91
+ c = Calculator.new
92
+ c.add_tags tags
93
+ c.sample s
94
+ self << c
86
95
  nil
87
96
  end
88
97
 
@@ -99,18 +108,6 @@ module Benelux
99
108
  def sd() merge.sd end
100
109
  def n() merge.n end
101
110
 
102
- def merge(*tags)
103
- tags = Selectable.normalize tags
104
- mc = Calculator.new
105
- mc.init_tags!
106
- all = tags.empty? ? self : self.filter(tags)
107
- all.each { |calc|
108
- mc.merge! calc
109
- mc.add_tags_quick calc.tags
110
- }
111
- mc
112
- end
113
-
114
111
  def filter(*tags)
115
112
  (f = super).name = @name
116
113
  f
@@ -172,7 +169,14 @@ module Benelux
172
169
  end
173
170
  @n+=1
174
171
  end
175
-
172
+
173
+ def first_tick() @last_time = Time.now end
174
+ def tick
175
+ tick_time = Time.now
176
+ sample(tick_time - @last_time)
177
+ @last_time = tick_time
178
+ end
179
+
176
180
  # Dump this Stats object with an optional additional message.
177
181
  def dump(msg = "", out=STDERR)
178
182
  out.puts "#{msg}: #{self.report}"
@@ -209,6 +213,7 @@ module Benelux
209
213
  end
210
214
 
211
215
  def ==(other)
216
+ return false unless self.class == other.class
212
217
  a=([@sum, @min, @max, @n, @sumsq] -
213
218
  [other.sum, other.min, other.max, other.n, other.sumsq])
214
219
  a.empty?
@@ -21,13 +21,11 @@ module Benelux
21
21
  include Selectable
22
22
 
23
23
  attr_accessor :ranges
24
- attr_accessor :counts
25
24
  attr_accessor :stats
26
25
  attr_accessor :default_tags
27
26
  attr_reader :caller
28
27
  def initialize(*args)
29
28
  @caller = Kernel.caller
30
- @counts = SelectableArray.new
31
29
  @ranges = SelectableArray.new
32
30
  @default_tags = Selectable::Tags.new
33
31
  @stats = Benelux::Stats.new
@@ -97,14 +95,6 @@ module Benelux
97
95
  end
98
96
  end
99
97
 
100
- def counts(name=nil, tags=Selectable::Tags.new)
101
- return @counts if name.nil?
102
- @counts.select do |count|
103
- ret = name.to_s == count.name.to_s &&
104
- (tags.nil? || count.tags >= tags)
105
- ret
106
- end
107
- end
108
98
  #
109
99
  # obj.regions(:do_request) =>
110
100
  #
@@ -133,14 +123,11 @@ module Benelux
133
123
  super
134
124
  end
135
125
 
136
- # TODO: Remove Benelux::Count. Just use Stats!
137
126
  def add_count(name, count, tags={})
138
127
  tags = tags.merge self.default_tags
139
- c = Benelux::Count.new(name, count)
140
- c.add_tags tags
141
128
  self.stats.add_group(name)
142
- self.stats.sample(name, count, c.tags)
143
- c
129
+ self.stats.sample(name, count, tags)
130
+ count
144
131
  end
145
132
 
146
133
  def add_mark(name)
data/lib/benelux.rb CHANGED
@@ -5,7 +5,7 @@ require 'thwait'
5
5
  require 'selectable'
6
6
 
7
7
  module Benelux
8
- VERSION = "0.4.2"
8
+ VERSION = "0.5.0"
9
9
  NOTSUPPORTED = [Class, Object, Kernel]
10
10
 
11
11
  class BeneluxError < RuntimeError; end
@@ -20,7 +20,6 @@ module Benelux
20
20
  require 'benelux/range'
21
21
  require 'benelux/stats'
22
22
  require 'benelux/packer'
23
- require 'benelux/reporter'
24
23
  require 'benelux/timeline'
25
24
  require 'benelux/mixins/thread'
26
25
  require 'benelux/mixins/symbol'
@@ -29,13 +28,13 @@ module Benelux
29
28
  attr_reader :packed_methods
30
29
  attr_reader :tracks
31
30
  attr_reader :timeline
32
- attr_reader :reporter
31
+ attr_reader :known_threads
33
32
  end
34
33
 
35
34
  @packed_methods = {}
36
35
  @tracks = SelectableHash.new
37
36
  @timeline = Timeline.new
38
- @reporter = Reporter.new
37
+ @known_threads = []
39
38
 
40
39
  @@mutex = Mutex.new
41
40
  @@debug = false
@@ -70,16 +69,25 @@ module Benelux
70
69
  @@mutex.synchronize do
71
70
  @tracks[name] ||= Track.new(name, group)
72
71
  @tracks[name].add_thread Thread.current
73
- @reporter.add_thread Thread.current
72
+ @known_threads << Thread.current
74
73
  end
75
74
  end
76
75
  Benelux.track(name)
77
76
  end
78
77
  Benelux.current_track :main
79
78
 
79
+ # Only updates data from threads that are dead
80
+ def Benelux.update_global_timeline
81
+ dthreads = Benelux.known_threads.select { |t|
82
+ !t.timeline.nil? && (t.nil? || !t.status)
83
+ }
84
+ # We don't need to make a special case for the
85
+ # current thread b/c the current is not dead.
86
+ Benelux.timeline.merge! *dthreads.collect { |t| t.timeline }
87
+ end
88
+
80
89
  # Thread tags become the default for any new Mark or Range.
81
90
  def Benelux.add_thread_tags(args=Selectable::Tags.new)
82
-
83
91
  Benelux.thread_timeline.add_default_tags args
84
92
  end
85
93
  def Benelux.add_thread_tag(*args) add_thread_tags *args end
@@ -102,15 +110,7 @@ module Benelux
102
110
 
103
111
 
104
112
  def Benelux.known_thread?(t=Thread.current)
105
- @reporter.thwait.threads.member? t
106
- end
107
-
108
- def Benelux.known_threads
109
- @reporter.thwait.threads
110
- end
111
-
112
- def Benelux.reporting_wait
113
- @reporter.wait
113
+ Benelux.known_threads.member? t
114
114
  end
115
115
 
116
116
  def Benelux.packed_method(klass, meth)
@@ -1,5 +1,5 @@
1
1
 
2
- tryouts "Array speed", :benchmark do
2
+ xtryouts "Array speed", :benchmark do
3
3
  set :base, []
4
4
 
5
5
  drill "Populate array" do
@@ -31,3 +31,27 @@ tryouts "Array speed", :benchmark do
31
31
  end
32
32
 
33
33
  end
34
+
35
+
36
+ tryouts "Array merge speed", :benchmark do
37
+ set :size, 10_000_000
38
+ set :merges, 100_000
39
+
40
+ drill "Create #{size} element Array" do
41
+ final = []
42
+ size.times { final << 1 }
43
+ end
44
+
45
+ drill "Merge #{size} element Array" do
46
+ all = []
47
+ msize = size / merges
48
+ merges.times do
49
+ a = []
50
+ msize.times { a << 1 }
51
+ all << a
52
+ end
53
+ final = []
54
+ all.each { |a| final.push *a }
55
+ end
56
+
57
+ end
@@ -0,0 +1,49 @@
1
+
2
+
3
+ require 'threadify'
4
+
5
+ parallel = 20
6
+ runcount = 100_000
7
+ baseline = []
8
+ testcase = []
9
+
10
+ def timer(&blk)
11
+ s = Time.now; blk.call; puts ('%.4f' % (Time.now - s).to_f)
12
+ end
13
+
14
+ print "Populate baseline: "
15
+ timer do
16
+ parallel.times do
17
+ seed = "a"
18
+ (runcount).times do
19
+ baseline << seed.succ!.clone
20
+ end
21
+ end
22
+ end
23
+
24
+ print "Start #{parallel} threads: "
25
+ threads = []
26
+ timer do
27
+ parallel.times do
28
+ threads << Thread.new do
29
+ seed = "a"
30
+ runcount.times { testcase << seed.succ!.clone }
31
+ end
32
+ end
33
+ end
34
+
35
+ print "Wait for threads: "
36
+ timer do
37
+ threads.each { |t| t.join }
38
+ end
39
+
40
+ # Compare baseline to testcase
41
+ p [:size, testcase.size == baseline.size]
42
+ p [:sorted, testcase.sort == baseline.sort]
43
+
44
+ # Q: Why does the testcase Array seem to repeat perfectly?
45
+ # [a,b,c,d,a,b,c,d,a,b,c,d]
46
+
47
+ __END__
48
+
49
+ Module.method_added?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: benelux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-06 00:00:00 -04:00
12
+ date: 2009-10-21 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -61,6 +61,7 @@ files:
61
61
  - tryouts/30_timeline_tryouts.rb
62
62
  - tryouts/proofs/alias_performance.rb
63
63
  - tryouts/proofs/array_performance.rb
64
+ - tryouts/proofs/thread_array.rb
64
65
  - tryouts/proofs/timer_threading.rb
65
66
  has_rdoc: true
66
67
  homepage: http://github.com/delano/benelux