starpu_trace_state_stats.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #!/usr/bin/python
  2. ##
  3. # StarPU --- Runtime system for heterogeneous multicore architectures.
  4. #
  5. # Copyright (C) 2016 INRIA
  6. #
  7. # StarPU is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU Lesser General Public License as published by
  9. # the Free Software Foundation; either version 2.1 of the License, or (at
  10. # your option) any later version.
  11. #
  12. # StarPU is distributed in the hope that it will be useful, but
  13. # WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. #
  16. # See the GNU Lesser General Public License in COPYING.LGPL for more details.
  17. ##
  18. ##
  19. # This script parses the generated trace.rec file and reports statistics about
  20. # the number of different events/tasks and their durations. The report is
  21. # similar to the starpu_paje_state_stats.in script, except that this one
  22. # doesn't need R and pj_dump (from the pajeng repository), and it is also much
  23. # faster.
  24. ##
  25. import getopt
  26. import os
  27. import sys
  28. class Event():
  29. def __init__(self, type, name, category, start_time):
  30. self._type = type
  31. self._name = name
  32. self._category = category
  33. self._start_time = start_time
  34. class EventStats():
  35. def __init__(self, name, duration_time, category, count = 1):
  36. self._name = name
  37. self._duration_time = duration_time
  38. self._category = category
  39. self._count = count
  40. def aggregate(self, duration_time):
  41. self._duration_time += duration_time
  42. self._count += 1
  43. def show(self):
  44. if not self._name == None:
  45. print "\"" + self._name + "\"," + str(self._count) + ",\"" + self._category + "\"," + str(round(self._duration_time, 6))
  46. class Worker():
  47. def __init__(self, id):
  48. self._id = id
  49. self._events = []
  50. self._stats = []
  51. self._stack = []
  52. def get_event_stats(self, name):
  53. for stat in self._stats:
  54. if stat._name == name:
  55. return stat
  56. return None
  57. def add_event(self, type, name, category, start_time):
  58. self._events.append(Event(type, name, category, start_time))
  59. def calc_stats(self):
  60. num_events = len(self._events) - 1
  61. for i in xrange(0, num_events):
  62. curr_event = self._events[i]
  63. next_event = self._events[i+1]
  64. if next_event._type == "PushState":
  65. self._stack.append(next_event)
  66. for j in xrange(i+1, num_events):
  67. next_event = self._events[j]
  68. if next_event._type == "SetState":
  69. break
  70. elif next_event._type == "PopState":
  71. curr_event = self._stack.pop()
  72. # Compute duration with the next event.
  73. a = curr_event._start_time
  74. b = next_event._start_time
  75. found = False
  76. for j in xrange(len(self._stats)):
  77. if self._stats[j]._name == curr_event._name:
  78. self._stats[j].aggregate(b - a)
  79. found = True
  80. break
  81. if not found == True:
  82. self._stats.append(EventStats(curr_event._name, b - a, curr_event._category))
  83. def read_blocks(input_file):
  84. empty_lines = 0
  85. first_line = 1
  86. blocks = []
  87. for line in open(input_file):
  88. if first_line:
  89. blocks.append([])
  90. blocks[-1].append(line)
  91. first_line = 0
  92. # Check for empty lines
  93. if not line or line[0] == '\n':
  94. # If 1st one: new block
  95. if empty_lines == 0:
  96. blocks.append([])
  97. empty_lines += 1
  98. else:
  99. # Non empty line: add line in current(last) block
  100. empty_lines = 0
  101. blocks[-1].append(line)
  102. return blocks
  103. def read_field(field, index):
  104. return field[index+1:-1]
  105. def insert_worker_event(workers, block):
  106. worker_id = -1
  107. name = None
  108. start_time = 0.0
  109. category = None
  110. for line in block:
  111. if line[:2] == "E:": # EventType
  112. event_type = read_field(line, 2)
  113. elif line[:2] == "C:": # Category
  114. category = read_field(line, 2)
  115. elif line[:2] == "W:": # WorkerId
  116. worker_id = int(read_field(line, 2))
  117. elif line[:2] == "N:": # Name
  118. name = read_field(line, 2)
  119. elif line[:2] == "S:": # StartTime
  120. start_time = float(read_field(line, 2))
  121. for worker in workers:
  122. if worker._id == worker_id:
  123. worker.add_event(event_type, name, category, start_time)
  124. return
  125. worker = Worker(worker_id)
  126. worker.add_event(event_type, name, category, start_time)
  127. workers.append(worker)
  128. def calc_times(stats):
  129. tr = 0.0 # Runtime
  130. tt = 0.0 # Task
  131. ti = 0.0 # Idle (likely Other)
  132. for stat in stats:
  133. if stat._category == None:
  134. continue
  135. if stat._category == "Runtime":
  136. tr += stat._duration_time
  137. elif stat._category == "Task":
  138. tt += stat._duration_time
  139. elif stat._category == "Other":
  140. ti += stat._duration_time
  141. else:
  142. sys.exit("Unknown category '" + stat._category + "'!")
  143. return (ti, tr, tt)
  144. def save_times(ti, tr, tt):
  145. f = open("times.csv", "w+")
  146. f.write("\"Time\",\"Duration\"\n")
  147. f.write("\"Runtime\"," + str(tr) + "\n")
  148. f.write("\"Task\"," + str(tt) + "\n")
  149. f.write("\"Other\"," + str(ti) + "\n")
  150. f.close()
  151. def calc_et(tt_1, tt_p):
  152. """ Compute the task efficiency (et). This measures the exploitation of
  153. data locality. """
  154. return tt_1 / tt_p
  155. def calc_er(tt_p, tr_p):
  156. """ Compute the runtime efficiency (er). This measures how the runtime
  157. overhead affects performance."""
  158. return tt_p / (tt_p + tr_p)
  159. def calc_ep(tt_p, tr_p, ti_p):
  160. """ Compute the pipeline efficiency (et). This measures how much
  161. concurrency is available and how well it's exploited. """
  162. return (tt_p + tr_p) / (tt_p + tr_p + ti_p)
  163. def calc_e(et, er, ep):
  164. """ Compute the parallel efficiency. """
  165. return et * er * ep
  166. def save_efficiencies(e, ep, er, et):
  167. f = open("efficiencies.csv", "w+")
  168. f.write("\"Efficiency\",\"Value\"\n")
  169. f.write("\"Parallel\"," + str(e) + "\n")
  170. f.write("\"Task\"," + str(et) + "\n")
  171. f.write("\"Runtime\"," + str(er) + "\n")
  172. f.write("\"Pipeline\"," + str(ep) + "\n")
  173. f.close()
  174. def usage():
  175. print "USAGE:"
  176. print "starpu_trace_state_stats.py [ -te -s=<time> ] <trace.rec>"
  177. print
  178. print "OPTIONS:"
  179. print " -t or --time Compute and dump times to times.csv"
  180. print
  181. print " -e or --efficiency Compute and dump efficiencies to efficiencies.csv"
  182. print
  183. print " -s or --seq_task_time Used to compute task efficiency between sequential and parallel times"
  184. print " (if not set, task efficiency will be 1.0)"
  185. print
  186. print "EXAMPLES:"
  187. print "# Compute event statistics and report them to stdout:"
  188. print "python starpu_trace_state_stats.py trace.rec"
  189. print
  190. print "# Compute event stats, times and efficiencies:"
  191. print "python starpu_trace_state_stats.py -te trace.rec"
  192. print
  193. print "# Compute correct task efficiency with the sequential task time:"
  194. print "python starpu_trace_state_stats.py -s=60093.950614 trace.rec"
  195. def main():
  196. try:
  197. opts, args = getopt.getopt(sys.argv[1:], "hets:",
  198. ["help", "time", "efficiency", "seq_task_time="])
  199. except getopt.GetoptError as err:
  200. usage()
  201. sys.exit(1)
  202. dump_time = False
  203. dump_efficiency = False
  204. tt_1 = 0.0
  205. for o, a in opts:
  206. if o in ("-h", "--help"):
  207. usage()
  208. sys.exit()
  209. elif o in ("-t", "--time"):
  210. dump_time = True
  211. elif o in ("-e", "--efficiency"):
  212. dump_efficiency = True
  213. elif o in ("-s", "--seq_task_time"):
  214. tt_1 = float(a)
  215. if len(args) < 1:
  216. usage()
  217. sys.exit()
  218. recfile = args[0]
  219. if not os.path.isfile(recfile):
  220. sys.exit("File does not exist!")
  221. # Declare a list for all workers.
  222. workers = []
  223. # Read the recutils file format per blocks.
  224. blocks = read_blocks(recfile)
  225. for block in blocks:
  226. if not len(block) == 0:
  227. first_line = block[0]
  228. if first_line[:2] == "E:":
  229. insert_worker_event(workers, block)
  230. # Compute worker statistics.
  231. stats = []
  232. for worker in workers:
  233. worker.calc_stats()
  234. for stat in worker._stats:
  235. found = False
  236. for s in stats:
  237. if stat._name == s._name:
  238. found = True
  239. break
  240. if not found == True:
  241. stats.append(EventStats(stat._name, 0.0, stat._category, 0))
  242. # Compute global statistics for all workers.
  243. for i in xrange(0, len(workers)):
  244. for stat in stats:
  245. s = workers[i].get_event_stats(stat._name)
  246. if not s == None:
  247. # A task might not be executed on all workers.
  248. stat._duration_time += s._duration_time
  249. stat._count += s._count
  250. # Output statistics.
  251. print "\"Name\",\"Count\",\"Type\",\"Duration\""
  252. for stat in stats:
  253. stat.show()
  254. # Compute runtime, task, idle times and dump them to times.csv
  255. ti_p = tr_p = tt_p = 0.0
  256. if dump_time == True:
  257. ti_p, tr_p, tt_p = calc_times(stats)
  258. save_times(ti_p, tr_p, tt_p)
  259. # Compute runtime, task, idle efficiencies and dump them to
  260. # efficiencies.csv.
  261. if dump_efficiency == True or not tt_1 == 0.0:
  262. if dump_time == False:
  263. ti_p, tr_p, tt_p = calc_times(stats)
  264. if tt_1 == 0.0:
  265. sys.stderr.write("WARNING: Task efficiency will be 1.0 because -s is not set!\n")
  266. tt_1 = tt_p
  267. # Compute efficiencies.
  268. ep = round(calc_ep(tt_p, tr_p, ti_p), 6)
  269. er = round(calc_er(tt_p, tr_p), 6)
  270. et = round(calc_et(tt_1, tt_p), 6)
  271. e = round(calc_e(et, er, ep), 6)
  272. save_efficiencies(e, ep, er, et)
  273. if __name__ == "__main__":
  274. main()