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 +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
|