Browse Source

tools: add a python script which parses the trace.rec file

This script can be used to parse the generated trace.rec to report
statistics about the number of events/tasks and their durations. The
output is similar to the starpu_paje_state_stats.in, except that this
one doesn't need a Paje trace and it doesn't depend on R and pj_dump
(from the pajeng repository). This starpu_trace_state_stats.py is also
4x much faster than the starpu_paje_state_stats.in script.

As for paje.trace, STARPU_GENERATE_TRACE=1 must be set to generate the
trace.rec file.

No manpages are installed for this tool.
Samuel Pitoiset 9 years ago
parent
commit
7ca57bdc80
2 changed files with 194 additions and 1 deletions
  1. 2 1
      tools/Makefile.am
  2. 192 0
      tools/starpu_trace_state_stats.py

+ 2 - 1
tools/Makefile.am

@@ -182,7 +182,8 @@ dist_bin_SCRIPTS +=			\
 	starpu_codelet_histo_profile	\
 	starpu_codelet_profile		\
 	starpu_paje_draw_histogram	\
-	starpu_paje_state_stats
+	starpu_paje_state_stats		\
+	starpu_trace_state_stats.py
 
 if STARPU_HAVE_WINDOWS
 STARPU_MSVC_dir		 =	$(bindir)

+ 192 - 0
tools/starpu_trace_state_stats.py

@@ -0,0 +1,192 @@
+#!/usr/bin/python
+
+##
+# StarPU --- Runtime system for heterogeneous multicore architectures.
+#
+# Copyright (C) 2016 INRIA
+#
+# StarPU is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+# StarPU is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# See the GNU Lesser General Public License in COPYING.LGPL for more details.
+##
+
+##
+# This script parses the generated trace.rec file and reports statistics about
+# the number of different events/tasks and their durations. The report is
+# similar to the starpu_paje_state_stats.in script, except that this one
+# doesn't need R and pj_dump (from the pajeng repository), and it is also much
+# faster.
+##
+
+import getopt
+import os
+import sys
+
+class Event():
+    def __init__(self, type, name, start_time):
+        self._type = type
+        self._name = name
+        self._start_time = start_time
+
+class EventStats():
+    def __init__(self, name, duration_time, count = 1):
+        self._name = name
+        self._duration_time = duration_time
+        self._count = count
+
+    def aggregate(self, duration_time):
+        self._duration_time += duration_time
+        self._count += 1
+
+    def show(self):
+        if not self._name == None:
+            print "\"" + self._name + "\"," + str(self._count) + "," + str(round(self._duration_time, 6))
+
+class Worker():
+    def __init__(self, id):
+        self._id        = id
+        self._events    = []
+        self._stats     = []
+        self._stack     = []
+
+    def get_event_stats(self, name):
+        for stat in self._stats:
+            if stat._name == name:
+                return stat
+        return None
+
+    def add_event(self, type, name, start_time):
+        self._events.append(Event(type, name, start_time))
+
+    def calc_stats(self):
+        num_events = len(self._events) - 1
+        for i in xrange(0, num_events):
+            curr_event = self._events[i]
+            next_event = self._events[i+1]
+
+            if next_event._type == "PushState":
+                self._stack.append(next_event)
+                for j in xrange(i+1, num_events):
+                    next_event = self._events[j]
+                    if next_event._type == "SetState":
+                        break
+            elif next_event._type == "PopState":
+                curr_event = self._stack.pop()
+
+            # Compute duration with the next event.
+            a = curr_event._start_time
+            b = next_event._start_time
+
+            found = False
+            for j in xrange(len(self._stats)):
+                if self._stats[j]._name == curr_event._name:
+                    self._stats[j].aggregate(b - a)
+                    found = True
+                    break
+            if not found == True:
+                self._stats.append(EventStats(curr_event._name, b - a))
+
+def read_blocks(input_file):
+    empty_lines = 0
+    first_line = 1
+    blocks = []
+    for line in open(input_file):
+        if first_line:
+            blocks.append([])
+            blocks[-1].append(line)
+            first_line = 0
+
+        # Check for empty lines
+        if not line or line[0] == '\n':
+            # If 1st one: new block
+            if empty_lines == 0:
+                blocks.append([])
+            empty_lines += 1
+        else:
+            # Non empty line: add line in current(last) block
+            empty_lines = 0
+            blocks[-1].append(line)
+    return blocks
+
+def read_field(field, index):
+    return field[index+1:-1]
+
+def insert_worker_event(workers, block):
+    worker_id = -1
+    name = None
+    start_time = 0.0
+
+    for line in block:
+        if line[:2] == "E:": # EventType
+            event_type = read_field(line, 2)
+        elif line[:2] == "W:": # WorkerId
+            worker_id = int(read_field(line, 2))
+        elif line[:2] == "N:": # Name
+            name = read_field(line, 2)
+        elif line[:2] == "S:": # StartTime
+            start_time = float(read_field(line, 2))
+
+    for worker in workers:
+        if worker._id == worker_id:
+            worker.add_event(event_type, name, start_time)
+            return
+    worker = Worker(worker_id)
+    worker.add_event(event_type, name, start_time)
+    workers.append(worker)
+
+def main():
+    if len(sys.argv) != 2:
+        print sys.argv[0] + " <trace.rec>"
+	sys.exit(1)
+    recfile = sys.argv[1]
+
+    if not os.path.isfile(recfile):
+        sys.exit("File does not exist!")
+
+    # Declare a list for all workers.
+    workers = []
+
+    # Read the recutils file format per blocks.
+    blocks = read_blocks(recfile)
+    for block in blocks:
+        if not len(block) == 0:
+            first_line = block[0]
+            if first_line[:2] == "E:":
+                insert_worker_event(workers, block)
+
+    # Compute worker statistics.
+    stats = []
+    for worker in workers:
+        worker.calc_stats()
+        for stat in worker._stats:
+            found = False
+            for s in stats:
+                if stat._name == s._name:
+                    found = True
+                    break
+            if not found == True:
+                stats.append(EventStats(stat._name, 0.0, 0))
+
+    # Compute global statistics for all workers.
+    for i in xrange(0, len(workers)):
+        for stat in stats:
+            s = workers[i].get_event_stats(stat._name)
+            if not s == None:
+                # A task might not be executed on all workers.
+                stat._duration_time += s._duration_time
+                stat._count += s._count
+
+    # Output statistics.
+    print "\"Value\",\"Events\",\"Duration\""
+    for stat in stats:
+        stat.show()
+
+if __name__ == "__main__":
+    main()