pwrake 0.9.7 → 0.9.8

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.
@@ -24,6 +24,27 @@ module Pwrake
24
24
  a.sort{|x,y| x[0]<=>y[0]}
25
25
  end
26
26
 
27
+ def count_start_end_from_csv_table(csvtable)
28
+ a = []
29
+ start_time = nil
30
+
31
+ csvtable.each do |row|
32
+ if row['command'] == 'pwrake_profile_start'
33
+ start_time = Time.parse(row['start_time'])
34
+ elsif row['command'] == 'pwrake_profile_end'
35
+ t = Time.parse(row['start_time']) - start_time
36
+ a << [t,0]
37
+ elsif start_time
38
+ t = Time.parse(row['start_time']) - start_time
39
+ a << [t,+1]
40
+ t = Time.parse(row['end_time']) - start_time
41
+ a << [t,-1]
42
+ end
43
+ end
44
+
45
+ a.sort{|x,y| x[0]<=>y[0]}
46
+ end
47
+
27
48
  def exec_density(a)
28
49
  reso = 0.1
29
50
  delta = 1/reso
@@ -98,51 +119,41 @@ plot '#{fpara}' w l axis x1y1 title 'parallelism'
98
119
  end
99
120
 
100
121
 
101
- def plot_parallelism2(file)
102
- a = count_start_end_from_csv(file)
122
+ def plot_parallelism2(csvtable, base)
123
+ a = count_start_end_from_csv_table(csvtable)
103
124
  return if a.size < 4
104
125
 
105
126
  density = exec_density(a)
106
127
 
107
- base = file.sub(/\.csv$/,"")
108
- fpara = base+"_para.dat"
109
- fdens = base+'_dens.dat'
110
128
  fimg = base+'.png'
111
129
 
112
- File.open(fdens,"w") do |f|
113
- density.each do |t,d|
114
- f.puts "#{t} #{d}"
115
- end
116
- end
117
-
118
130
  n = a.size
119
131
  i = 0
120
132
  y = 0
121
133
  y_max = 0
122
134
 
123
- File.open(fpara,"w") do |f|
124
- begin
125
- t = 0
126
- y_pre = 0
127
- n.times do |i|
128
- if a[i][0]-t > 0.001
129
- f.printf "%.3f %d\n", t, y_pre
130
- t = a[i][0]
131
- f.printf "%.3f %d\n", t, y
132
- end
133
- y += a[i][1]
134
- y_pre = y
135
- y_max = y if y > y_max
135
+ para = []
136
+ begin
137
+ t = 0
138
+ y_pre = 0
139
+ n.times do |i|
140
+ if a[i][0]-t > 0.001
141
+ para.push "%.3f %d" % [t, y_pre]
142
+ t = a[i][0]
143
+ para.push "%.3f %d" % [t, y]
136
144
  end
137
- rescue
138
- p a[i]
145
+ y += a[i][1]
146
+ y_pre = y
147
+ y_max = y if y > y_max
139
148
  end
149
+ rescue
150
+ p a[i]
140
151
  end
141
152
 
142
153
  t_end = (a.last)[0]
143
154
 
144
155
  IO.popen("gnuplot","r+") do |f|
145
- f.puts "
156
+ f.print "
146
157
  set terminal png
147
158
  set output '#{fimg}'
148
159
  #set rmargin 10
@@ -156,8 +167,16 @@ set y2label 'exec/sec'
156
167
  set arrow 1 from #{t_end},#{y_max*0.5} to #{t_end},0 linecolor rgb 'blue'
157
168
  set label 1 \"#{t_end}\\nsec\" at first #{t_end},#{y_max*0.5} right front textcolor rgb 'blue'
158
169
 
159
- plot '#{fpara}' w l axis x1y1 title 'parallelism', '#{fdens}' w l axis x1y2 title 'exec/sec'
170
+ plot '-' w l axis x1y1 title 'parallelism', '-' w l axis x1y2 title 'exec/sec'
160
171
  "
172
+ para.each do |x|
173
+ f.puts x
174
+ end
175
+ f.puts "e"
176
+
177
+ density.each do |t,d|
178
+ f.puts "#{t} #{d}"
179
+ end
161
180
  end
162
181
 
163
182
  #puts "Parallelism plot: #{fimg}"
@@ -188,6 +207,112 @@ plot '#{fpara}' w l axis x1y1 title 'parallelism', '#{fdens}' w l axis x1y2 titl
188
207
  a
189
208
  end
190
209
 
210
+
211
+ def get_command_key(s, pattern)
212
+ pattern.each do |cmd,regex|
213
+ if regex =~ s
214
+ return cmd
215
+ end
216
+ end
217
+ if /\(([^()]+)\)/ =~ s
218
+ s = $1
219
+ end
220
+ a = s.split(/;/)
221
+ a.each do |x|
222
+ if /^\s*(\S+)/ =~ x
223
+ k = $1
224
+ next if k=='cd'
225
+ return k
226
+ end
227
+ end
228
+ nil
229
+ end
230
+
231
+ def count_start_end_by_pattern(csvtable, pattern)
232
+ h = Hash.new
233
+ start_time = nil
234
+
235
+ csvtable.each do |row|
236
+ command = row['command']
237
+ if command == 'pwrake_profile_start'
238
+ start_time = Time.parse(row['start_time'])
239
+ elsif command == 'pwrake_profile_end'
240
+ t = Time.parse(row['start_time']) - start_time
241
+ h.values.each do |a|
242
+ a << [t,0]
243
+ end
244
+ elsif start_time
245
+ cmd = get_command_key(command, pattern)
246
+ h[cmd] ||= []
247
+ t = Time.parse(row['start_time']) - start_time
248
+ h[cmd] << [t,+1]
249
+ t = Time.parse(row['end_time']) - start_time
250
+ h[cmd] << [t,-1]
251
+ end
252
+ end
253
+
254
+ h.each do |k,a|
255
+ a.sort!{|x,y| x[0]<=>y[0]}
256
+ end
257
+ h
258
+ end
259
+
260
+ def plot_parallelism_by_pattern(csvtable, base, pattern)
261
+ y_max = 0
262
+ t_end = 0
263
+ para = {}
264
+ t_pat = count_start_end_by_pattern(csvtable, pattern)
265
+ t_pat.each do |cmd,a|
266
+ n = a.size
267
+ i = 0
268
+ y = 0
269
+ para[cmd] = dat = []
270
+ begin
271
+ t = 0
272
+ y_pre = 0
273
+ n.times do |i|
274
+ if a[i][0]-t > 0.001
275
+ dat.push "%.3f %d" % [t, y_pre]
276
+ t = a[i][0]
277
+ dat.push "%.3f %d" % [t, y]
278
+ end
279
+ y += a[i][1]
280
+ y_pre = y
281
+ y_max = y if y > y_max
282
+ end
283
+ rescue
284
+ p a[i]
285
+ end
286
+ if (a.last)[0] > t_end
287
+ t_end = (a.last)[0]
288
+ end
289
+ end
290
+
291
+ fimg = base+'_para_cmd.png'
292
+
293
+ IO.popen("gnuplot","r+") do |f|
294
+ #begin f = $stdout
295
+ f.print "
296
+ set terminal png
297
+ set output '#{fimg}'
298
+ set title '#{base}'
299
+ set xlabel 'time (sec)'
300
+ set ylabel 'parallelism'
301
+ "
302
+ f.print "plot "
303
+ f.puts para.map{|cmd,re| "'-' w l title '#{cmd}'"}.join(",")
304
+ para.each do |cmd,dat|
305
+ dat.each do |x|
306
+ f.puts x
307
+ end
308
+ f.puts "e"
309
+ end
310
+ end
311
+
312
+ #puts "Parallelism plot: #{fimg}"
313
+ fimg
314
+ end
315
+
191
316
  def timeline_to_grid(a)
192
317
  resolution = Rational(1,10)
193
318
 
@@ -209,7 +334,7 @@ plot '#{fpara}' w l axis x1y1 title 'parallelism', '#{fdens}' w l axis x1y2 titl
209
334
  return grid
210
335
  end
211
336
 
212
- def plot_parallelizm_by_host(csvtable,base)
337
+ def plot_parallelism_by_host(csvtable,base)
213
338
  fpng = base+"_para_host.png"
214
339
  data = read_time_by_host_from_csv(csvtable)
215
340
  return fpng if data.size == 0
@@ -50,10 +50,12 @@ EOL
50
50
 
51
51
  @sh_table = CSV.read(@csv_file,:headers=>true)
52
52
  h = {}
53
+ @elap_sum = 0
53
54
  @sh_table.each do |row|
54
55
  if host = row['host']
55
56
  h[host] = true
56
57
  end
58
+ @elap_sum += row['elap_time'].to_f
57
59
  end
58
60
  @hosts = h.keys.sort
59
61
  @start_time = Time.parse(@sh_table[0]["start_time"])
@@ -153,7 +155,9 @@ EOL
153
155
  html << "<table>\n"
154
156
  html << "<tr><th>log file</th><td>#{@base}</td><tr>\n"
155
157
  html << "<tr><th>ncore</th><td>#{@ncore}</td><tr>\n"
156
- html << "<tr><th>elapsed time(sec)</th><td>#{@elap}</td><tr>\n"
158
+ html << "<tr><th>elapsed time</th><td>%.3f sec</td><tr>\n"%[@elap]
159
+ html << "<tr><th>cumulative process time</th><td>%.3f sec</td><tr>\n"%[@elap_sum]
160
+ html << "<tr><th>occupancy</th><td>%.3f %%</td><tr>\n"%[@elap_sum/@elap/@ncore*100]
157
161
  html << "<tr><th>start time</th><td>#{@start_time}</td><tr>\n"
158
162
  html << "<tr><th>end time</th><td>#{@end_time}</td><tr>\n"
159
163
  html << "</table>\n"
@@ -164,12 +168,16 @@ EOL
164
168
  end
165
169
  html << "</table>\n"
166
170
  html << "<h2>Parallelism</h2>\n"
167
- fimg = Parallelism.plot_parallelism2(@csv_file)
168
- html << "<img src='#{fimg}' align='top'/></br>\n"
171
+ fimg = Parallelism.plot_parallelism2(@sh_table,@base)
172
+ html << "<img src='./#{File.basename(fimg)}' align='top'/></br>\n"
173
+
174
+ html << "<h2>Parallelism by command</h2>\n"
175
+ fimg3 = Parallelism.plot_parallelism_by_pattern(@sh_table,@base,@pattern)
176
+ html << "<img src='./#{File.basename(fimg3)}' align='top'/></br>\n"
169
177
 
170
178
  html << "<h2>Parallelism by host</h2>\n"
171
- fimg2 = Parallelism.plot_parallelizm_by_host(@sh_table,@base)
172
- html << "<img src='#{fimg2}' align='top'/></br>\n"
179
+ fimg2 = Parallelism.plot_parallelism_by_host(@sh_table,@base)
180
+ html << "<img src='./#{File.basename(fimg2)}' align='top'/></br>\n"
173
181
 
174
182
  html << "<h2>Command statistics</h2>\n"
175
183
  html << "<table>\n"
@@ -182,7 +190,7 @@ EOL
182
190
  html << "</tr>\n"
183
191
  end
184
192
  html << "<table>\n"
185
- html << "<img src='#{histogram_plot}' align='top'/></br>\n"
193
+ html << "<img src='./#{File.basename(histogram_plot)}' align='top'/></br>\n"
186
194
 
187
195
  task_locality
188
196
  html << "<h2>Locality statistics</h2>\n"
@@ -29,7 +29,7 @@ module Pwrake
29
29
  end
30
30
  html << "</table>\n"
31
31
  html << "<h2>Elapsed time</h2>\n"
32
- html << "<img src='#{@elap_png}' align='top'/></br>\n"
32
+ html << "<img src='./#{File.basename(@elap_png)}' align='top'/></br>\n"
33
33
 
34
34
  html << "<h2>Histogram of Execution time</h2>\n"
35
35
  html << report_histogram()
@@ -97,7 +97,7 @@ plot #{a}/x,'-' w lp lw 2 ps 2 title 'elapsed time'
97
97
  html << "<tr><td>#{id}</td><td>#{r.ncore}</td>" + s.html_td + "</tr>\n"
98
98
  end
99
99
  html << "</table>\n"
100
- html << "<img src='./#{@images[cmd]}'/>\n"
100
+ html << "<img src='./#{File.basename(@images[cmd])}'/>\n"
101
101
  end
102
102
  html
103
103
  end
data/lib/pwrake/shell.rb CHANGED
@@ -19,9 +19,6 @@ module Pwrake
19
19
  @@current_id = 0
20
20
  @@profiler = Profiler.new
21
21
 
22
- # @@global_lock = Mutex.new
23
- # @@last_exec_time = Time.now
24
-
25
22
  def self.profiler
26
23
  @@profiler
27
24
  end
@@ -35,7 +32,6 @@ module Pwrake
35
32
  @work_dir = @option[:work_dir] || Dir.pwd
36
33
  @pass_env = @option[:pass_env]
37
34
  @ssh_opt = @option[:ssh_opt]
38
- # @ssh_min_exec_interval = Pwrake.application.pwrake_options['SHELL_MIN_EXEC_INTERVAL'].to_f
39
35
  @terminator = ""
40
36
  TLEN.times{ @terminator << CHARS[rand(CHARS.length)] }
41
37
  end
@@ -152,19 +148,6 @@ module Pwrake
152
148
  raise "@io is closed" if @io.closed?
153
149
  status = nil
154
150
 
155
- # t0 = Time.now
156
- # @@global_lock.synchronize do
157
- # t = Time.now - @@last_exec_time
158
- # if t < @ssh_min_exec_interval
159
- # sleep @ssh_min_exec_interval-t
160
- # end
161
- # @@last_exec_time = Time.now
162
- # end
163
- # t = Time.now-t0
164
- # #if t>0.001
165
- # Log.info "-- locktime %.6f"%t
166
- # #end
167
-
168
151
  start_time = Time.now
169
152
  begin
170
153
  @io.puts @@profiler.command(cmd,@terminator)
@@ -187,4 +170,17 @@ module Pwrake
187
170
  end
188
171
  end
189
172
 
173
+
174
+ class NoActionShell < Shell
175
+ def initialize()
176
+ @host = '(noaction)'
177
+ @@current_id += 1
178
+ @id = @@current_id
179
+ end
180
+ def start
181
+ end
182
+ def finish
183
+ end
184
+ end
185
+
190
186
  end # module Pwrake
@@ -3,6 +3,50 @@ module Pwrake
3
3
  InvocationChain = Rake::InvocationChain
4
4
  TaskArguments = Rake::TaskArguments
5
5
 
6
+ class RankStat
7
+ def initialize
8
+ @lock = Mutex.new
9
+ @stat = []
10
+ end
11
+
12
+ def add_sample(rank,elap)
13
+ @lock.synchronize do
14
+ stat = @stat[rank]
15
+ if stat.nil?
16
+ @stat[rank] = stat = [0,0.0]
17
+ end
18
+ stat[0] += 1
19
+ stat[1] += elap
20
+ Log.debug "--- add_sample rank=#{rank} stat=#{stat.inspect} weight=#{stat[0]/stat[1]}"
21
+ end
22
+ end
23
+
24
+ def rank_weight
25
+ @lock.synchronize do
26
+ sum = 0.0
27
+ count = 0
28
+ weight = @stat.map do |stat|
29
+ if stat
30
+ w = stat[0]/stat[1]
31
+ sum += w
32
+ count += 1
33
+ w
34
+ else
35
+ nil
36
+ end
37
+ end
38
+ if count == 0
39
+ avg = 1.0
40
+ else
41
+ avg = sum/count
42
+ end
43
+ [weight, avg]
44
+ end
45
+ end
46
+ end
47
+
48
+ RANK_STAT = RankStat.new
49
+
6
50
  module TaskAlgorithm
7
51
 
8
52
  def location
@@ -63,7 +107,7 @@ module Pwrake
63
107
  timer = Timer.new("search_task")
64
108
  h = application.pwrake_options['HALT_QUEUE_WHILE_SEARCH']
65
109
  application.task_queue.synchronize(h) do
66
- search_with_call_chain(self, task_args, InvocationChain::EMPTY)
110
+ search_with_call_chain(nil, task_args, InvocationChain::EMPTY)
67
111
  end
68
112
  timer.finish
69
113
  end
@@ -73,24 +117,28 @@ module Pwrake
73
117
  if shell = Pwrake.current_shell
74
118
  shell.current_task = self
75
119
  end
76
-
77
120
  @lock.synchronize do
78
121
  return if @already_invoked
79
122
  @already_invoked = true
80
123
  end
81
124
  pw_execute(@arg_data) if needed?
125
+ Log.debug("pw_execute: #{Time.now-time_start} sec, name=#{name}")
82
126
  if kind_of?(Rake::FileTask)
83
- application.postprocess(self) # <---------
127
+ t = Time.now
128
+ application.postprocess(self)
129
+ Log.debug("postprocess: #{Time.now-t} sec, name=#{name}")
84
130
  if File.exist?(name)
131
+ t = Time.now
85
132
  @file_stat = File::Stat.new(name)
133
+ Log.debug("File::Stat: #{Time.now-t} sec, name=#{name}")
86
134
  end
87
135
  end
88
136
  log_task(time_start)
89
137
  t = Time.now
90
138
  application.finish_queue.enq(self)
91
139
  shell.current_task = nil if shell
92
- pw_enq_subsequents # <---------
93
- Log.debug "--- pw_invoke (#{name}) postprocess time=#{Time.now-t} sec"
140
+ pw_enq_subsequents
141
+ Log.debug "--- pw_invoke (#{name}) enq_subseq time=#{Time.now-t} sec"
94
142
  end
95
143
 
96
144
  def log_task(time_start)
@@ -104,10 +152,12 @@ module Pwrake
104
152
  end
105
153
  return if !application.task_logger
106
154
 
107
- row = [ @task_id, name,
108
- time_start, time_end, time_end-time_start,
109
- @prerequisites.join('|')
110
- ]
155
+ elap = time_end - time_start
156
+ if !@actions.empty? && kind_of?(Rake::FileTask)
157
+ RANK_STAT.add_sample(rank,elap)
158
+ end
159
+
160
+ row = [ @task_id, name, time_start, time_end, elap, @prerequisites.join('|') ]
111
161
 
112
162
  if loc
113
163
  row << loc.join('|')
@@ -190,9 +240,12 @@ module Pwrake
190
240
  def pw_enq_subsequents
191
241
  @lock.synchronize do
192
242
  t = Time.now
193
- @subsequents.each do |t| # <<--- competition !!!
194
- if t && t.check_prereq_finished(self.name)
195
- application.task_queue.enq(t)
243
+ h = application.pwrake_options['HALT_QUEUE_WHILE_SEARCH']
244
+ application.task_queue.synchronize(h) do
245
+ @subsequents.each do |t| # <<--- competition !!!
246
+ if t && t.check_prereq_finished(self.name)
247
+ application.task_queue.enq(t)
248
+ end
196
249
  end
197
250
  end
198
251
  @already_finished = true # <<--- competition !!!
@@ -217,11 +270,12 @@ module Pwrake
217
270
 
218
271
  return true if @already_finished # <<--- competition !!!
219
272
  @subsequents ||= []
220
- @subsequents << subseq # <<--- competition !!!
273
+ @subsequents << subseq if subseq # <<--- competition !!!
221
274
 
222
275
  if ! @already_searched
223
276
  @already_searched = true
224
277
  @arg_data = task_args
278
+ @lock_rank = Monitor.new
225
279
  if @prerequisites.empty?
226
280
  @unfinished_prereq = {}
227
281
  else
@@ -262,16 +316,56 @@ module Pwrake
262
316
  end
263
317
  private :format_search_flags
264
318
 
319
+ def rank
320
+ @lock_rank.synchronize do
321
+ if @rank.nil?
322
+ if @subsequents.nil? || @subsequents.empty?
323
+ @rank = 0
324
+ else
325
+ max_rank = 0
326
+ @subsequents.each do |subsq|
327
+ r = subsq.rank
328
+ if max_rank < r
329
+ max_rank = r
330
+ end
331
+ end
332
+ if @actions.empty? || !kind_of?(Rake::FileTask)
333
+ step = 0
334
+ else
335
+ step = 1
336
+ end
337
+ @rank = max_rank + step
338
+ end
339
+ Log.debug "--- Task[#{name}] rank=#{@rank.inspect}"
340
+ end
341
+ end
342
+ @rank
343
+ end
344
+
265
345
  def file_size
266
346
  @file_stat ? @file_stat.size : 0
267
347
  end
268
348
 
269
- def prior?
349
+ def file_mtime
350
+ @file_stat ? @file_stat.mtime : Time.at(0)
351
+ end
352
+
353
+ def input_file_size
354
+ unless @input_file_size
355
+ @input_file_size = 0
356
+ @prerequisites.each do |preq|
357
+ @input_file_size += application[preq].file_size
358
+ end
359
+ end
360
+ @input_file_size
361
+ end
362
+
363
+ def has_input_file?
270
364
  kind_of?(Rake::FileTask) && !@prerequisites.empty?
271
365
  end
272
366
 
273
367
  def suggest_location
274
- if prior? && @suggest_location.nil?
368
+ if has_input_file? && @suggest_location.nil?
275
369
  @suggest_location = []
276
370
  loc_fsz = Hash.new(0)
277
371
  @prerequisites.each do |preq|
@@ -297,6 +391,33 @@ module Pwrake
297
391
  @suggest_location
298
392
  end
299
393
 
394
+ def input_file_mtime
395
+ if has_input_file? && @input_file_mtime.nil?
396
+ hash = Hash.new
397
+ max_sz = 0
398
+ @prerequisites.each do |preq|
399
+ t = application[preq]
400
+ sz = t.file_size
401
+ if sz > 0
402
+ hash[t] = sz
403
+ if sz > max_sz
404
+ max_sz = sz
405
+ end
406
+ end
407
+ end
408
+ half_max_sz = max_sz / 2
409
+ hash.each do |t,sz|
410
+ if sz > half_max_sz
411
+ time = t.file_mtime
412
+ if @input_file_mtime.nil? || @input_file_mtime < time
413
+ @input_file_mtime = time
414
+ end
415
+ end
416
+ end
417
+ end
418
+ @input_file_mtime
419
+ end
420
+
300
421
  def suggest_location2
301
422
  if kind_of?(Rake::FileTask) && preq_name = @prerequisites[0]
302
423
  application[preq_name].location