bottlegraph.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import sim, os, collections
  2. # On-line collection of bottle graphs [Du Bois, OOSPLA 2013]
  3. # See tools/bottlegraph.py for a version that works on off-line data
  4. class BottleGraph:
  5. def setup(self, args):
  6. self.total_runtime = 0
  7. self.runtime = collections.defaultdict(long)
  8. self.contrib = collections.defaultdict(long)
  9. self.running = collections.defaultdict(bool)
  10. self.time_last = 0
  11. self.in_roi = False
  12. def update(self, time):
  13. if not self.in_roi:
  14. return
  15. nthreads = sum([ 1 if running else 0 for running in self.running.values() ])
  16. if time > self.time_last:
  17. time_interval = (time - self.time_last) / 1e6 # Count everything in nanoseconds
  18. self.total_runtime += time_interval
  19. if nthreads:
  20. for thread, running in self.running.items():
  21. if running:
  22. self.runtime[thread] += time_interval
  23. self.contrib[thread] += time_interval / float(nthreads)
  24. self.time_last = time
  25. def hook_roi_begin(self):
  26. self.time_last = sim.stats.time()
  27. self.in_roi = True
  28. def hook_roi_end(self):
  29. self.update(sim.stats.time())
  30. self.in_roi = False
  31. def hook_thread_start(self, threadid, time):
  32. self.update(time)
  33. self.running[threadid] = True
  34. def hook_thread_exit(self, threadid, time):
  35. self.update(time)
  36. self.running[threadid] = False
  37. def hook_thread_stall(self, threadid, reason, time):
  38. if reason != 'unscheduled':
  39. self.update(time)
  40. self.running[threadid] = False
  41. def hook_thread_resume(self, threadid, threadby, time):
  42. self.update(time)
  43. self.running[threadid] = True
  44. def hook_sim_end(self):
  45. xs = dict([ (thread, self.runtime[thread] / self.contrib[thread]) for thread in self.runtime ])
  46. ys = dict([ (thread, self.contrib[thread] / 1e9) for thread in self.runtime ])
  47. runtime = self.total_runtime / 1e9
  48. # Threads in descending order of parallelism
  49. threads = sorted(self.runtime.keys(), key = lambda thread: xs[thread], reverse = True)
  50. #for thread in threads:
  51. # print '[BOTTLEGRAPH]', thread, '%.5f' % ys[thread], '%.5f' % xs[thread], sim.thread.get_thread_name(thread)
  52. max_x = int(max(xs.values()) + 1.2)
  53. max_y = runtime * 1.1
  54. fd = open('%s/bottlegraph.input' % sim.config.output_dir, 'w')
  55. print >> fd, '''\
  56. set terminal png font "FreeSans,10" size 450,400
  57. set output "bottlegraph.png"
  58. set grid
  59. set xlabel "Parallelism"
  60. set ylabel "Runtime (seconds)"
  61. set key outside bottom horizontal
  62. set style fill solid 1.0 noborder
  63. set xrange [-%d:%d]
  64. set yrange [0:%f]
  65. set xtics (%s) nomirror
  66. ''' % (max_x, max_x, max_y, ','.join([ '"%d" %d' % (abs(x), x) for x in range(-max_x, max_x+1) ]))
  67. y = 0
  68. colors = ('#00FFFF', '#A52A2A', '#A9A9A9', '#FF1493', '#8FBC8F', '#FF6347', '#006400')
  69. color = lambda i: colors[i % len(colors)]
  70. for i, thread in enumerate(threads):
  71. print >> fd, 'set object rect from %f,%f to %f,%f fc rgb "%s"' % (-xs[thread], y, xs[thread], y+ys[thread], color(i))
  72. y += ys[thread]
  73. print >> fd, 'plot %s' % ', '.join([
  74. '-1 with boxes title "%s" lc rgb "%s"' % (sim.thread.get_thread_name(thread), color(i))
  75. for i, thread in reversed(list(enumerate(threads)))
  76. if ys[thread] > .01 * runtime
  77. ])
  78. fd.close()
  79. os.system('cd "%s" && gnuplot bottlegraph.input' % sim.config.output_dir)
  80. sim.util.register(BottleGraph())