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 +15 -0
- data/benelux.gemspec +2 -1
- data/lib/benelux/packer.rb +3 -3
- data/lib/benelux/reporter.rb +26 -22
- data/lib/benelux/stats.rb +41 -36
- data/lib/benelux/timeline.rb +2 -15
- data/lib/benelux.rb +15 -15
- data/tryouts/proofs/array_performance.rb +25 -1
- data/tryouts/proofs/thread_array.rb +49 -0
- metadata +3 -2
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
|
+
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
|
|
data/lib/benelux/packer.rb
CHANGED
@@ -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 =
|
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 =
|
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 =
|
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
|
}
|
data/lib/benelux/reporter.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
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.
|
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
|
-
|
49
|
-
a += other.send(name)
|
50
|
-
a
|
51
|
+
s.add_group name
|
52
|
+
s.group(name) << other.group(name)
|
51
53
|
end
|
52
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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?
|
data/lib/benelux/timeline.rb
CHANGED
@@ -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,
|
143
|
-
|
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.
|
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 :
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
+
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-
|
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
|