pwrake 0.9.3

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.
@@ -0,0 +1,113 @@
1
+ module Pwrake
2
+
3
+ class Graphviz
4
+
5
+ def initialize
6
+ @nodes = []
7
+ @edges = []
8
+ # @node_id = {}
9
+ @filenode_id = {}
10
+ @tasknode_id = {}
11
+ @node_name = {}
12
+ @count = 0
13
+ @traced = {}
14
+ end
15
+
16
+ attr_reader :filenode_id, :tasknode_id, :node_name
17
+
18
+ def trace( name = :default, target = nil )
19
+ traced_cond = @traced[name]
20
+
21
+ task = Rake.application[name]
22
+
23
+ #if task.kind_of?(Rake::FileTask)
24
+ if task.kind_of?(Rake::Task)
25
+ push_filenode( name )
26
+ if !task.actions.empty? and !traced_cond
27
+ push_tasknode( name )
28
+ push_taskedge( name )
29
+ end
30
+ push_fileedge( name, target )
31
+ target = name
32
+ end
33
+ @traced[name] = true
34
+
35
+ if !traced_cond
36
+ task.prerequisites.each_with_index do |prereq,i|
37
+ trace( prereq, target )
38
+ end
39
+ end
40
+ end
41
+
42
+ def trim( name )
43
+ name = name.to_s
44
+ name = File.basename(name)
45
+ name.sub(/H\d+/,'').sub(/object\d+/,"")
46
+ end
47
+
48
+ def push_filenode( name )
49
+ if @filenode_id[name].nil?
50
+ tag = "T#{@count}"
51
+ @count += 1
52
+ @filenode_id[name] = tag
53
+ @node_name[tag] = name
54
+ @nodes.push "#{tag} [label=\"#{trim(name)}\", shape=box];"
55
+ end
56
+ end
57
+
58
+ def push_tasknode( name )
59
+ if @tasknode_id[name].nil?
60
+ tag = "T#{@count}"
61
+ @count += 1
62
+ @tasknode_id[name] = tag
63
+ @node_name[tag] = name
64
+ label = Rake.application[name].comment
65
+ @nodes.push "#{tag} [label=\"#{label}\", shape=ellipse];"
66
+ end
67
+ end
68
+
69
+ def push_fileedge( name, target )
70
+ if target
71
+ if n2 = @tasknode_id[target]
72
+ n1 = @filenode_id[name]
73
+ elsif n1 = @tasknode_id[name]
74
+ n2 = @filenode_id[target]
75
+ else
76
+ n1 = @filenode_id[name]
77
+ n2 = @filenode_id[target]
78
+ end
79
+ @edges.push "#{n1} -> #{n2};"
80
+ end
81
+ end
82
+
83
+ def push_taskedge( name )
84
+ if n1 = @tasknode_id[name]
85
+ n2 = @filenode_id[name]
86
+ @edges.push "#{n1} -> #{n2};"
87
+ end
88
+ end
89
+
90
+ def write(file)
91
+ open(file, "w") do |w|
92
+ #w.puts "digraph sample {\ngraph [size=\"12,100\",ranksep=1.5,nodesep=0.2];"
93
+ w.puts "digraph sample {"
94
+ w.puts "graph [size=\"70,70\", rankdir=LR];"
95
+ @nodes.each do |x|
96
+ w.puts x
97
+ end
98
+ @edges.each do |x|
99
+ w.puts x
100
+ end
101
+ w.puts "}"
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ task "graphviz", :file do |t,a|
108
+ file = a[:file] || 'pwrake.dot'
109
+ g = Pwrake::Graphviz.new
110
+ g.trace
111
+ g.write(file)
112
+ $stderr.puts "Wrote task graph to `#{file}'"
113
+ end
@@ -0,0 +1,254 @@
1
+ module Pwrake
2
+
3
+ module TaskAlgorithm
4
+ def assigned
5
+ @assigned ||= []
6
+ end
7
+ end
8
+
9
+
10
+ class LocalityAwareQueue < TaskQueue
11
+
12
+ class Throughput
13
+
14
+ def initialize(list=nil)
15
+ @interdomain_list = {}
16
+ @interhost_list = {}
17
+ if list
18
+ values = []
19
+ list.each do |x,y,v|
20
+ hash_x = (@interdomain_list[x] ||= {})
21
+ hash_x[y] = n = v.to_f
22
+ values << n
23
+ end
24
+ @min_value = values.min
25
+ else
26
+ @min_value = 1
27
+ end
28
+ end
29
+
30
+ def interdomain(x,y)
31
+ hash_x = (@interdomain_list[x] ||= {})
32
+ if v = hash_x[y]
33
+ return v
34
+ elsif v = (@interdomain_list[y] || {})[x]
35
+ hash_x[y] = v
36
+ else
37
+ if x == y
38
+ hash_x[y] = 1
39
+ else
40
+ hash_x[y] = 0.1
41
+ end
42
+ end
43
+ hash_x[y]
44
+ end
45
+
46
+ def interhost(x,y)
47
+ return @min_value if !x
48
+ hash_x = (@interhost_list[x] ||= {})
49
+ if v = hash_x[y]
50
+ return v
51
+ elsif v = (@interhost_list[y] || {})[x]
52
+ hash_x[y] = v
53
+ else
54
+ x_short, x_domain = parse_hostname(x)
55
+ y_short, y_domain = parse_hostname(y)
56
+ v = interdomain(x_domain,y_domain)
57
+ hash_x[y] = v
58
+ end
59
+ hash_x[y]
60
+ end
61
+
62
+ def parse_hostname(host)
63
+ /^([^.]*)\.?(.*)$/ =~ host
64
+ [$1,$2]
65
+ end
66
+
67
+ end # class Throughput
68
+
69
+
70
+ def initialize(hosts,opt={})
71
+ super(opt)
72
+ @hosts = hosts
73
+ @throughput = Throughput.new
74
+ @size = 0
75
+ @q = nil
76
+ @q1 = []
77
+ @q2 = {}
78
+ @hosts.each{|h| @q2[h]=[]}
79
+ @q2[nil] = []
80
+ @enable_steal = !opt['disable_steal']
81
+ @time_prev = Time.now
82
+
83
+ @thread = Thread.new{thread_loop}
84
+ end
85
+
86
+ attr_reader :size
87
+
88
+
89
+ def thread_loop
90
+ while !@finished
91
+ bulk_mvq
92
+ sleep # time_out
93
+ end
94
+ end
95
+
96
+
97
+ def time_out
98
+ if @size == 0
99
+ if @q1.size == 1
100
+ 0.25
101
+ else
102
+ 0.5
103
+ end
104
+ else
105
+ if @q1.size > @size
106
+ 0.5
107
+ elsif @q1.size > @size/2
108
+ 1.0
109
+ else
110
+ 3.0
111
+ end
112
+ end
113
+ end
114
+
115
+
116
+ def bulk_mvq
117
+ a = nil
118
+ @mutex.synchronize do
119
+ return if @q1.empty?
120
+
121
+ time_now = Time.now
122
+ interval = time_now-@time_prev
123
+ return if time_now-@time_prev < time_out
124
+
125
+ log_bulk_mvq
126
+ @time_prev = time_now
127
+
128
+ a = @q1
129
+ @q1 = []
130
+ end
131
+
132
+ where(a)
133
+
134
+ @mutex.synchronize do
135
+ while t = a.shift
136
+ mvq(t)
137
+ end
138
+ @cv.broadcast
139
+ end
140
+ end
141
+
142
+ def log_bulk_mvq
143
+ msg = @q1[0..2].map{|t| t.name}.inspect
144
+ msg.sub!(/]$/,",...") if @q1.size > 3
145
+ Log.info "-- bulk_mvq interval=%.6fs @size=#{@size} @q1.size=#{@q1.size} @q1=#{msg}"%(Time.now-@time_prev)
146
+ end
147
+
148
+
149
+ def where(task_list)
150
+ # implemented in child class
151
+ end
152
+
153
+
154
+ def mvq(t)
155
+ stored = false
156
+ if t.respond_to? :location
157
+ t.location.each do |h|
158
+ if q = @q2[h]
159
+ t.assigned.push(h)
160
+ q.push(t)
161
+ stored = true
162
+ end
163
+ end
164
+ end
165
+ if !stored
166
+ @q2[@hosts[rand(@hosts.size)]].push(t)
167
+ # @q2[nil].push(t)
168
+ end
169
+ @size += 1
170
+ end
171
+
172
+
173
+ def enq_impl(item,hint=nil)
174
+ #Log.debug "--- #{self.class}#enq_impl #{item.inspect}"
175
+ @q1.push(item)
176
+ end
177
+
178
+ def enq_finish
179
+ @thread.run if @thread.alive?
180
+ end
181
+
182
+ def deq_impl(host,n)
183
+ @thread.run if @thread.alive?
184
+
185
+ if t = deq_locate(host)
186
+ Log.info "-- deq_locate n=#{n} task=#{t.name} host=#{host}"
187
+ return t
188
+ end
189
+
190
+ if @enable_steal && n > 1
191
+ if t = deq_steal(host)
192
+ Log.info "-- deq_steal n=#{n} task=#{t.name} host=#{host}"
193
+ return t
194
+ end
195
+ end
196
+
197
+ m = 0.05*(2**([n,5].min))
198
+ @cv.wait(@mutex,m)
199
+ nil
200
+ end
201
+
202
+
203
+ def deq_locate(host)
204
+ q = @q2[host]
205
+ if q && !q.empty?
206
+ t = q.shift
207
+ t.assigned.each{|x| @q2[x].delete_if{|x| t.equal? x}}
208
+ @size -= 1
209
+ return t
210
+ else
211
+ nil
212
+ end
213
+ end
214
+
215
+ def deq_steal(host)
216
+ # select a task based on many and close
217
+ max_host = nil
218
+ max_num = 0
219
+ @q2.each do |h,a|
220
+ if !a.empty?
221
+ d = @throughput.interhost(host,h) * a.size
222
+ if d > max_num
223
+ max_host = h
224
+ max_num = d
225
+ end
226
+ end
227
+ end
228
+ if max_host
229
+ deq_locate(max_host)
230
+ else
231
+ deq_locate(nil)
232
+ end
233
+ end
234
+
235
+ def size
236
+ @q1.size + @size
237
+ end
238
+
239
+ def clear
240
+ @q1.clear
241
+ @hosts.each{|h| @q2[h].clear}
242
+ end
243
+
244
+ def empty?
245
+ @q1.empty? && @hosts.all?{|h| @q2[h].empty?}
246
+ end
247
+
248
+ def finish
249
+ super
250
+ @thread.run if @thread.alive?
251
+ end
252
+
253
+ end
254
+ end
@@ -0,0 +1,153 @@
1
+ module Pwrake
2
+
3
+ LOCK = Mutex.new
4
+
5
+ class Logger
6
+
7
+ attr_accessor :level
8
+
9
+ module Severity
10
+ # Low-level information, mostly for developers
11
+ DEBUG = 0
12
+ INFO = 1
13
+ WARN = 2
14
+ ERROR = 3
15
+ FATAL = 4
16
+ UNKNOWN = 5
17
+ end
18
+ include Severity
19
+
20
+ def initialize
21
+ @level = WARN
22
+ @out = nil
23
+ @filename = nil
24
+ @lock = Mutex.new
25
+ end
26
+
27
+ def open(file)
28
+ close if @out
29
+ case file
30
+ when IO
31
+ @out = file
32
+ @filename = nil
33
+ when String
34
+ @out = File.open(file,"w")
35
+ @filename = file
36
+ else
37
+ raise "file arg must be IO or String"
38
+ end
39
+ @start_time = Time.now
40
+ info "LogStart=" + fmt_time(@start_time)
41
+ info "logfile=#{@filename}" if @filename
42
+ end
43
+
44
+ def finish(str, start_time)
45
+ if @out
46
+ finish_time = Time.now
47
+ t1 = Log.fmt_time(start_time)
48
+ t2 = Log.fmt_time(finish_time)
49
+ elap = finish_time - start_time
50
+ info "#{str} : start=#{t1} end=#{t2} elap=#{elap}"
51
+ end
52
+ end
53
+
54
+ def add(severity, message)
55
+ if !severity || severity >= @level
56
+ if @out
57
+ @lock.synchronize do
58
+ @out.write(message+"\n")
59
+ end
60
+ else
61
+ LOCK.synchronize do
62
+ $stderr.write(message+"\n")
63
+ end
64
+ end
65
+ end
66
+ true
67
+ end
68
+ alias log add
69
+
70
+ def info(msg)
71
+ add(INFO, msg)
72
+ end
73
+
74
+ def debug(msg)
75
+ add(DEBUG, msg)
76
+ end
77
+
78
+ def warn(msg)
79
+ add(WARN, msg)
80
+ end
81
+
82
+ def fmt_time(t)
83
+ t.strftime("%Y-%m-%dT%H:%M:%S.%%06d") % t.usec
84
+ end
85
+
86
+ def timer(prefix,*args)
87
+ Timer.new(prefix,*args)
88
+ end
89
+
90
+ def close
91
+ finish "LogEnd", @start_time
92
+ @lock.synchronize do
93
+ @out.close if @filename
94
+ @out=nil
95
+ end
96
+ @filename=nil
97
+ end
98
+
99
+ end # class Logger
100
+
101
+
102
+ LOGGER = Logger.new
103
+
104
+ module Log
105
+ include Logger::Severity
106
+
107
+ module_function
108
+
109
+ def open(file)
110
+ LOGGER.open(file)
111
+ end
112
+
113
+ def close
114
+ LOGGER.close
115
+ end
116
+
117
+ def info(s)
118
+ LOGGER.info(s)
119
+ end
120
+
121
+ def debug(s)
122
+ LOGGER.debug(s)
123
+ end
124
+
125
+ def warn(s)
126
+ LOGGER.warn(s)
127
+ end
128
+
129
+ def level
130
+ LOGGER.level
131
+ end
132
+
133
+ def level=(x)
134
+ LOGGER.level = x
135
+ end
136
+
137
+ def fmt_time(t)
138
+ t.strftime("%Y-%m-%dT%H:%M:%S.%%06d") % t.usec
139
+ end
140
+
141
+ def timer(prefix,*args)
142
+ Timer.new(prefix,*args)
143
+ end
144
+
145
+ def output_message(message)
146
+ LOCK.synchronize do
147
+ $stderr.write(message+"\n")
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+ end # module Pwrake
@@ -0,0 +1,109 @@
1
+ module Pwrake
2
+
3
+ def current_shell
4
+ Thread.current[:shell]
5
+ end
6
+
7
+ def current_shell=(a)
8
+ Thread.current[:shell] = a
9
+ end
10
+
11
+ module_function :current_shell, :current_shell=
12
+
13
+
14
+ class Master
15
+ include Pwrake::Option
16
+
17
+ attr_reader :task_queue
18
+ attr_reader :shell_set
19
+
20
+ def initialize
21
+ end
22
+
23
+ def init
24
+ init_option # Pwrake::Option
25
+ end
26
+
27
+ def setup
28
+ setup_option # Pwrake::Option
29
+ end
30
+
31
+ def start
32
+ @counter = Counter.new
33
+ @task_queue = @queue_class.new(@core_list)
34
+ @task_queue.enable_steal = !Rake.application.options.disable_steal
35
+ @shell_set = []
36
+ @core_list.each_with_index do |h,i|
37
+ @shell_set << @shell_class.new(h,@shell_opt)
38
+ end
39
+ start_threads
40
+ end
41
+
42
+ def finish
43
+ Log.debug "-- Master#finish called"
44
+ @task_queue.finish if @task_queue
45
+ @threads.each{|t| t.join }
46
+ @counter.print
47
+ finish_option # Pwrake::Option
48
+ end
49
+
50
+ def start_threads
51
+ Thread.abort_on_exception = true
52
+ @threads = []
53
+ @shell_set.each do |c|
54
+ @threads << Thread.new(c) do |conn|
55
+ Pwrake.current_shell = conn
56
+ conn.start
57
+ begin
58
+ thread_loop(conn)
59
+ ensure
60
+ Log.info "-- worker[#{conn.id}] ensure : closing #{conn.host}"
61
+ conn.finish
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def thread_loop(conn,last=nil)
68
+ @task_queue.reserve(last) if last
69
+ hint = (conn) ? conn.host : nil
70
+ standard_exception_handling do
71
+ while t = @task_queue.deq(hint)
72
+ Log.debug "-- Master#thread_loop deq t=#{t.inspect}"
73
+ t.pw_invoke
74
+ return if t == last
75
+ end
76
+ end
77
+ end
78
+
79
+ # Provide standard execption handling for the given block.
80
+ def standard_exception_handling
81
+ begin
82
+ yield
83
+ rescue SystemExit => ex
84
+ # Exit silently with current status
85
+ @task_queue.stop
86
+ raise
87
+ rescue OptionParser::InvalidOption => ex
88
+ # Exit silently
89
+ @task_queue.stop
90
+ exit(false)
91
+ rescue Exception => ex
92
+ # Exit with error message
93
+ name = "pwrake"
94
+ $stderr.puts "#{name} aborted!"
95
+ $stderr.puts ex.message
96
+ if Rake.application.options.trace
97
+ $stderr.puts ex.backtrace.join("\n")
98
+ else
99
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
100
+ $stderr.puts "(See full trace by running task with --trace)"
101
+ end
102
+ @task_queue.stop
103
+ exit(false)
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ end # module Pwrake