gen_topology.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #!/usr/bin/env python
  2. import sys, os, math, collections, sniper_lib, sniper_stats, sniper_config, getopt
  3. def gen_topology(resultsdir = '.', jobid = None, outputobj = sys.stdout, format = 'svg', embedded = False):
  4. names = ('hwcontext', 'smt', 'L1-I', 'L1-D', 'L2', 'L3', 'L4', 'tag-dir', 'nuca-cache', 'dram-cache', 'dram-cntlr')
  5. ids = dict([ (name, collections.defaultdict(lambda: None)) for name in names ])
  6. stats = sniper_stats.SniperStats(resultsdir, jobid)
  7. config = sniper_lib.get_config(resultsdir = resultsdir, jobid = jobid)
  8. try:
  9. topology = stats.get_topology()
  10. except:
  11. print >> sys.stderr, "Failed getting topology information"
  12. topology = None
  13. max_id = 0
  14. if topology:
  15. for name, lid, mid in stats.get_topology():
  16. if name not in names:
  17. print >> sys.stderr, 'Unknown component', name
  18. continue
  19. ids[name][int(lid)] = int(mid)
  20. max_id = max(max_id, int(lid))
  21. def format_config(name, lid):
  22. caches = {'L1-I': 'l1_icache', 'L1-D': 'l1_dcache', 'L2': 'l2_cache', 'L3': 'l3_cache', 'L4': 'l4_cache'}
  23. if name in caches:
  24. value = sniper_config.get_config(config, 'perf_model/%s/cache_size' % caches[name], lid)
  25. return sniper_lib.format_size(1024 * long(value), digits = 0)
  26. elif name == 'dram-cache':
  27. value = sniper_config.get_config(config, 'perf_model/dram/cache/cache_size', lid)
  28. return sniper_lib.format_size(1024 * long(value), digits = 0)
  29. else:
  30. return ''
  31. if format == 'text':
  32. print >> outputobj, ' '*20,
  33. for lid in range(max_id+1):
  34. print >> outputobj, '%3d' % lid,
  35. print >> outputobj
  36. for name in names:
  37. if ids[name].keys():
  38. print >> outputobj, '%-20s' % name,
  39. for lid in range(max_id+1):
  40. mid = ids[name][lid]
  41. if mid is None:
  42. value = ' '
  43. elif mid == lid:
  44. value = 'X'
  45. else:
  46. value = '<'
  47. print >> outputobj, '%3s' % value,
  48. print >> outputobj
  49. elif format == 'svg':
  50. class Svg:
  51. def __init__(self):
  52. self.margin_x = 50; self.step_x = 110
  53. self.margin_y = 50; self.step_y = 50
  54. self.size_x = 0; self.size_y = 0
  55. self.items = []
  56. def paint_box(self, (x, y), (w, h), name = '', label = 0, color = '#ffffff', zorder = 0, margin = (.2, .2), root = (0, 0)):
  57. x += root[0]; y += root[1]
  58. self.size_x = max(self.size_x, (x+w) * self.step_x); self.size_y = max(self.size_y, (y+h) * self.step_y)
  59. svg = '''\
  60. <rect id="%s" x="%d" y="%d" width="%d" height="%d" rx="0"
  61. style="stroke:#000000;stroke-width:1;stroke-linejoin:miter; stroke-linecap:butt;fill:%s;"/>
  62. ''' % (name, self.margin_x + x * self.step_x, self.margin_y + y * self.step_y,
  63. (w - margin[0]) * self.step_x, (h - margin[1]) * self.step_y, color)
  64. if label:
  65. svg += '''\
  66. <text xml:space="preserve" x="%d" y="%d" fill="#000000" font-family="Times" font-style="normal" font-weight="normal" font-size="12" text-anchor="start">%s</text>
  67. ''' % (self.margin_x + (x + .1) * self.step_x, self.margin_y + (y + .3) * self.step_y, label)
  68. self.items.append((zorder, svg))
  69. def write(self, outputobj):
  70. if not embedded:
  71. print >> outputobj, '''\
  72. <?xml version="1.0" standalone="no"?>
  73. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  74. '''
  75. print >> outputobj, '''\
  76. <svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d">
  77. <g style="stroke-width:.025in; fill:none">
  78. ''' % (self.size_x + 2*self.margin_x, self.size_y + 2*self.margin_y)
  79. for order, svg in sorted(self.items, reverse = True):
  80. print >> outputobj, svg
  81. print >> outputobj, '''\
  82. </g>
  83. </svg>
  84. '''
  85. svg = Svg()
  86. ymax = None
  87. is_mesh = (sniper_config.get_config(config, 'network/memory_model_1') == 'emesh_hop_by_hop')
  88. if is_mesh:
  89. ncores = int(config['general/total_cores'])
  90. dimensions = int(sniper_config.get_config(config, 'network/emesh_hop_by_hop/dimensions'))
  91. concentration = int(sniper_config.get_config(config, 'network/emesh_hop_by_hop/concentration'))
  92. if dimensions == 1:
  93. width, height = int(math.ceil(1.0 * ncores / concentration)), 1
  94. else:
  95. if config.get('network/emesh_hop_by_hop/size'):
  96. width, height = map(int, sniper_config.get_config(config, 'network/emesh_hop_by_hop/size').split(':'))
  97. else:
  98. width = int(math.sqrt(ncores / concentration))
  99. height = int(math.ceil(1.0 * ncores / concentration / width))
  100. assert width * height * concentration == ncores
  101. def lid_tile_root(lid):
  102. return lid - lid % concentration
  103. def xpos(lid):
  104. return _xpos[lid] - _xpos[lid_tile_root(lid)]
  105. def tile_root(lid):
  106. return ((concentration + .25) * (int(lid / concentration) % width), (ymax + 1) * (lid / concentration / width))
  107. else:
  108. def xpos(lid):
  109. return _xpos[lid]
  110. def tile_root(lid):
  111. return (0, 0)
  112. if topology:
  113. _xpos = range(max_id+1)
  114. if ids['smt'].keys():
  115. x = -1
  116. for lid in range(max_id+1):
  117. if ids['smt'][lid] == lid:
  118. x += 1
  119. _xpos[lid] = x
  120. ypos = [ 0 for _ in range(max_id+1) ]
  121. for lid in range(max_id+1):
  122. mid = ids['smt'][lid]
  123. ypos[mid] += .7
  124. y = max(ypos) + .3
  125. else:
  126. y = 1
  127. ymin = y
  128. for name in names:
  129. if name in ('hwcontext', 'smt'): continue
  130. if ids[name]:
  131. y += 1
  132. ymax = y
  133. y = ymin
  134. if ids['smt'].keys():
  135. for lid in range(max_id+1):
  136. svg.paint_box((xpos(lid)+.05, ypos[mid]+.1), (1-.1, 1-.2), 'core-%d' % lid, 'Core #%d' % lid, root = tile_root(lid))
  137. for lid in range(max_id+1):
  138. if ids['smt'][lid] == lid:
  139. svg.paint_box((xpos(lid), 0), (1, ypos[mid] + .3), 'smt-%d' % lid, color = '#cccccc', zorder = 1, root = tile_root(lid))
  140. else:
  141. for lid in range(max_id+1):
  142. svg.paint_box((xpos(lid), 0), (1, 1), 'core-%d' % lid, 'Core #%d' % lid, root = tile_root(lid))
  143. for name in names:
  144. if name in ('hwcontext', 'smt'): continue
  145. if ids[name]:
  146. size = 0
  147. for lid in range(max_id, -1, -1):
  148. if not ids['smt'] or ids['smt'][lid] == lid:
  149. size += 1
  150. if ids[name][lid] == lid:
  151. cfg = format_config(name, lid)
  152. if cfg:
  153. label = '%s (%s)' % (name, cfg)
  154. else:
  155. label = name
  156. if is_mesh:
  157. size = min(size, concentration)
  158. svg.paint_box((xpos(lid), y), (size, 1), '%s-%d' % (name, lid), label, root = tile_root(lid))
  159. if name == 'dram-cntlr' and not is_mesh:
  160. svg.paint_box((xpos(lid)-.075, -.2), (size+.15, y+1+.4), color = '#dddddd', zorder = 2, root = tile_root(lid))
  161. size = 0
  162. y += 1
  163. if is_mesh:
  164. for lid in range(0, max_id+1, concentration):
  165. svg.paint_box((xpos(lid)-.075, -.2), (concentration+.15, y+.4), color = '#dddddd', zorder = 2, root = tile_root(lid))
  166. y += 1
  167. if is_mesh:
  168. y *= height
  169. else:
  170. y = 0
  171. if is_mesh:
  172. results = sniper_lib.get_results(resultsdir = resultsdir, jobid = jobid)['results']
  173. if 'dram-queue.total-time-used' in results \
  174. and 'network.shmem-1.mesh.link-up.num-requests' in results:
  175. import gridcolors
  176. time0 = max(results['performance_model.elapsed_time'])
  177. def util2color(utilization):
  178. return '#%02x%02x%02x' % gridcolors.colorscale(utilization)
  179. OFFSET_Y = y
  180. SCALE_X = .6
  181. BOXSIZE = .2
  182. for y in range(height):
  183. for x in range(width):
  184. for c in range(concentration):
  185. if c > 0:
  186. continue
  187. core = (y * width + x) * concentration + c
  188. svg.paint_box((x*SCALE_X, y+OFFSET_Y), (SCALE_X*.8, 1*.8), 'node-%d-%d' % (x, y), margin = (0, 0))
  189. for link, _x, _y in (
  190. ('down', 0, -.5), ('up', 0, .5),
  191. ('left', -.5, 0), ('right', .5, 0),
  192. ('out', -.15, -.2), ('in', .15, -.2),
  193. ):
  194. res = results['network.shmem-1.mesh.link-%s.num-requests' % link]
  195. if core < len(res) and res[core] > 0:
  196. utilization = results['network.shmem-1.mesh.link-%s.total-time-used' % link][core] / float(time0)
  197. color = util2color(utilization)
  198. svg.paint_box(((x + (.5 + _x)*.8 - BOXSIZE/2.)*SCALE_X, y + (.5 + _y)*.8 - BOXSIZE/2. + OFFSET_Y), (BOXSIZE*SCALE_X, BOXSIZE), 'link-%d-%d-%s' % (x, y, link), color = color, zorder = -1, margin = (0, 0))
  199. if results['dram-queue.num-requests'][core] > 0:
  200. utilization = results['dram-queue.total-time-used'][core] / float(time0)
  201. color = util2color(utilization)
  202. _x, _y = 0, .15
  203. svg.paint_box(((x + (.5 + _x)*.8 - 1.5*BOXSIZE/2.)*SCALE_X, y + (.5 + _y)*.8 - BOXSIZE/2. + OFFSET_Y), (1.5*BOXSIZE*SCALE_X, BOXSIZE), 'link-%d-%d-%s' % (x, y, link), color = color, zorder = -1, margin = (0, 0))
  204. svg.write(outputobj)
  205. if __name__ == '__main__':
  206. resultsdir = '.'
  207. jobid = None
  208. outputfilename = None
  209. formatdefaultoutputfile = {'svg': 'topo.svg', 'text': 'topo.txt'}
  210. validformats = ('svg', 'text')
  211. format = 'svg'
  212. def usage():
  213. print 'Usage: %s [-h|--help (help)] [-d <resultsdir (.)> | -j <jobid>] [-o|--output (output filename/"-" for stdout)] [-f|--format (options: %s)]' % (sys.argv[0], validformats)
  214. try:
  215. opts, args = getopt.getopt(sys.argv[1:], "hd:j:o:f:", [ "help", "output=", "format=" ])
  216. except getopt.GetoptError, e:
  217. print e
  218. usage()
  219. sys.exit()
  220. for o, a in opts:
  221. if o == '-h' or o == '--help':
  222. usage()
  223. sys.exit()
  224. elif o == '-d':
  225. resultsdir = a
  226. elif o == '-j':
  227. jobid = long(a)
  228. elif o == '-o' or o == '--output':
  229. outputfilename = a
  230. elif o == '-f' or o == '--format':
  231. if a not in validformats:
  232. print >> sys.stderr, '%s is not a valid format' % a
  233. usage()
  234. sys.exit()
  235. format = a
  236. else:
  237. usage()
  238. sys.exit()
  239. if outputfilename == None:
  240. outputfilename = formatdefaultoutputfile[format]
  241. if outputfilename == '-':
  242. output = sys.stdout
  243. else:
  244. output = open(outputfilename, 'w')
  245. gen_topology(resultsdir = resultsdir, jobid = jobid, outputobj = output, format = format)