benelux 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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