pwrake 0.9.7 → 0.9.8

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