summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugeni Dodonov <eugeni.dodonov@intel.com>2011-11-05 10:50:20 -0200
committerEugeni Dodonov <eugeni.dodonov@intel.com>2011-11-09 11:44:32 -0200
commit79c6987116cb9ded646b8bf775f458436f20ff4a (patch)
treecf83e5d8580b34c708d2809b8c45bebaf97c5a25
parent3ae38a52d31014cf7ae3cf9f43f51e50468144c0 (diff)
intel_gpu_analyser: Now generate some interesting results.
Now parsing command line options, generating summaries and some interesting charts. Signed-off-by: Eugeni Dodonov <eugeni.dodonov@intel.com>
-rwxr-xr-xtools/intel_gpu_analyser.py342
1 files changed, 326 insertions, 16 deletions
diff --git a/tools/intel_gpu_analyser.py b/tools/intel_gpu_analyser.py
index a5b92f1..b1e0bf6 100755
--- a/tools/intel_gpu_analyser.py
+++ b/tools/intel_gpu_analyser.py
@@ -5,19 +5,76 @@
import sys
import os
+import getopt
+import tempfile
-def collect(command):
+# for charts
+import pylab
+
+HEADER="""
+<html>
+<head>
+<title>Analysis for %(title)s</title>
+<style>
+body {
+ font-family: arial;
+}
+
+p {
+ margin-left: 10px;
+}
+</style>
+</head>
+
+<body>
+<h1>Analysis for %(title)s</h1>
+
+<h2>Summary:</h2>
+<p>
+<h3>Execution lasted %(duration).2f seconds</h3>
+"""
+
+MINMAXAVG="""
+<p>
+<h3>%(descr)s:</h3>
+<b>Minimum:</b> %(min)s <br/>
+<b>Maximum:</b> %(max)s <br/>
+<b>Agerage:</b> %(avg)s <br/>
+</p>
+"""
+
+FIGURE="""
+<p>
+<hr/>
+<b>%(title)s</b>
+<br/>
+<a href="%(img)s"><img src="%(img)s" width=800 height=600></a>
+</p>
+"""
+
+TAIL="""
+</body>
+</html>
+"""
+
+def print_header(output):
+ """Prints the header"""
+ print >>output, HEADER
+
+def collect(fd):
"""Collects statistics for a given command"""
- print "Running %s" % command
- fd = os.popen("intel_gpu_top -o - -e \"%s\"" % command)
results = {'time': [],
'utime': [],
'stime': [],
'power': [],
'render': [],
+ 'render_ops': [],
'bitstr': [],
+ 'bitstr_ops': [],
'bitstr2': [],
- 'blitter': []
+ 'bitstr2_ops': [],
+ 'blitter': [],
+ 'blitter_ops': []
}
while True:
line = fd.readline()
@@ -27,29 +84,282 @@ def collect(command):
if line[0] == "#":
continue
data = line.split()
+ # TODO: detect column names from headers
results['time'].append(float(data[0]))
results['utime'].append(float(data[1]))
results['stime'].append(float(data[2]))
results['power'].append(float(data[3]))
results['render'].append(float(data[4]))
+ results['render_ops'].append(int(data[5]))
results['bitstr'].append(float(data[6]))
+ results['bitstr_ops'].append(int(data[7]))
results['bitstr2'].append(float(data[8]))
+ results['bitstr2_ops'].append(int(data[9]))
results['blitter'].append(float(data[10]))
+ results['blitter_ops'].append(int(data[11]))
return results
-def analyse(results):
+def analyse(results, title, out_dir, summary="index.html"):
"""Analyses intel_gpu_top results"""
- minpower = min(results['power'])
- maxpower = max(results['power'])
- avgpower = sum(results['power']) / len(results['power'])
- print "Power: min %f, max %f, avg %f" % (minpower, maxpower, avgpower)
- for iter in results:
- pass
+ # calculate min/max/avg values
+ keys = results.keys()
+ for iter in keys:
+ if not results[iter]:
+ print "ERROR: no results collected for '%s', aborting" % iter
+ sys.exit(1)
+ results["%s_min" % iter] = min(results[iter])
+ results["%s_max" % iter] = max(results[iter])
+ results["%s_avg" % iter] = sum(results[iter]) / len(results[iter])
+ # start composing the output
+ output = open("%s/%s" % (out_dir, summary), "w")
+ print >>output, HEADER % {'title': title,
+ 'duration': results['time'][-1]
+ }
+
+ # print summaries
+ for iter, descr in [('utime', 'User time % of CPU'),
+ ('stime', 'System time % of CPU'),
+ ('power', 'Power consumption (W)'),
+ ('render', 'Render engine usage % of GPU'),
+ ('render_ops', 'Render engine ops per interval'),
+ ('bitstr', 'Bitstream engine usage % of GPU'),
+ ('bitstr_ops', 'Bitstream engine ops per interval'),
+ ('bitstr2', 'Bitstream 6 engine usage % of GPU'),
+ ('bitstr2_ops', 'Bitstream 6 engine ops per interval'),
+ ('blitter', 'Blitter engine usage % of GPU'),
+ ('blitter_ops', 'Blitter engine ops per interval'),
+ ]:
+ minval = results['%s_min' % iter]
+ maxval = results['%s_max' % iter]
+ avgval = results['%s_avg' % iter]
+ if minval == maxval == avgval:
+ # No variation in data, skipping
+ print "No variations in %s, skipping" % iter
+ continue
+ minval_s = "%.2f" % minval
+ maxval_s = "%.2f" % maxval
+ avgval_s = "%.2f" % avgval
+ print >>output, MINMAXAVG % {
+ 'descr': descr,
+ 'min': minval_s,
+ 'max': maxval_s,
+ 'avg': avgval_s,
+ }
+
+ # graphics
+ fig = pylab.figure()
+ ax = pylab.subplot(111)
+ pylab.title("Summary of CPU/GPU/Power usage")
+ pylab.ylabel("Usage (%)")
+ pylab.xlabel("Time (s)")
+
+ ax.plot(results['time'], results['utime'], label="User time")
+ ax.plot(results['time'], results['stime'], label="System time")
+ num_axis = 2
+ for ring in ["render", "bitstr", "bitstr2", "blitter"]:
+ if results["%s_avg" % ring] == -1:
+ print "No data for %s, skipping" % ring
+ continue
+ ax.plot(results['time'], results[ring], label=ring)
+ num_axis += 1
+ # Do we have power?
+ if results["power_avg"] != -1:
+ # plotting power
+ ax2 = ax.twinx()
+ ax2.plot(results['time'], results["power"], label="Power (W)", color='red')
+ ax2.set_ylabel('Watts')
+
+ pylab.grid()
+ # Shink current axis's height by 10% on the bottom
+ box = ax.get_position()
+ ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
+ ax.legend(loc = 'upper center', ncol=num_axis, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1))
+ pylab.savefig("%s/plot_summary.png" % out_dir, format="png", dpi=200)
+ print >>output, FIGURE % {'img': 'plot_summary.png',
+ 'title': 'Summary of the execution, outlining the CPU usage (in user and system mode), per-ring GPU usage, and power usage (if available) '
+ }
+
+ # graphics ops/per/second
+ fig = pylab.figure()
+ ax = pylab.subplot(111)
+ pylab.title("GPU rings ops per interval")
+ pylab.ylabel("Ops (n)")
+ pylab.xlabel("Time (s)")
+
+ num_axis = 0
+ for ring in ["render_ops", "bitstr_ops", "bitstr2_ops", "blitter_ops"]:
+ if results["%s_avg" % ring] == -1:
+ print "No data for %s, skipping" % ring
+ continue
+ ax.plot(results['time'], results[ring], label=ring)
+ num_axis += 1
+
+ pylab.grid()
+ # Shink current axis's height by 10% on the bottom
+ box = ax.get_position()
+ ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
+ ax.legend(loc = 'upper center', ncol=num_axis, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1))
+ pylab.savefig("%s/plot_gpu_ops.png" % out_dir, format="png", dpi=200)
+ print >>output, FIGURE % {'img': 'plot_gpu_ops.png',
+ 'title': 'Ops per interval for each GPU ring during the execution'
+ }
+
+ # power
+ fig = pylab.figure()
+ pylab.title("Power usage summary")
+ pylab.ylabel("Power (W)")
+ pylab.xlabel("Time (s)")
+
+ pylab.plot(results['power'], label="Power")
+ pylab.grid()
+ # Shink current axis's height by 10% on the bottom
+ pylab.savefig("%s/plot_power.png" % out_dir, format="png", dpi=200)
+ print >>output, FIGURE % {'img': 'plot_power.png',
+ 'title': 'Power usage over the course of execution'
+ }
+
+ # power vs CPU
+ fig = pylab.figure()
+ ax = pylab.subplot(111)
+ pylab.title("CPU vs Power usage")
+ pylab.ylabel("Usage (%)")
+ pylab.xlabel("Time (s)")
+
+ ax.plot(results['time'], results['utime'], label="User time")
+ ax.plot(results['time'], results['stime'], label="System time")
+ # plotting power
+ ax2 = ax.twinx()
+ ax2.plot(results['time'], results["power"], label="Power (W)", color='red')
+ ax2.set_ylabel('Watts')
+
+ pylab.grid()
+ # Shink current axis's height by 10% on the bottom
+ box = ax.get_position()
+ ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
+ ax.legend(loc = 'upper center', ncol=3, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1))
+ pylab.savefig("%s/plot_power_cpu.png" % out_dir, format="png", dpi=200)
+ print >>output, FIGURE % {'img': 'plot_power_cpu.png',
+ 'title': 'Power utilization co-related to CPU'
+ }
+
+ # power vs GPU
+ fig = pylab.figure()
+ ax = pylab.subplot(111)
+ pylab.title("GPU vs Power usage")
+ pylab.ylabel("Usage (%)")
+ pylab.xlabel("Time (s)")
+
+ num_axis = 0
+ for ring in ["render", "bitstr", "bitstr2", "blitter"]:
+ if results["%s_avg" % ring] == -1:
+ print "No data for %s, skipping" % ring
+ continue
+ ax.plot(results['time'], results[ring], label=ring)
+ num_axis += 1
+ # Do we have power?
+ # plotting power
+ ax2 = ax.twinx()
+ ax2.plot(results['time'], results["power"], label="Power (W)", color='red')
+ ax2.set_ylabel('Watts')
+ num_axis += 1
+
+ pylab.grid()
+ # Shink current axis's height by 10% on the bottom
+ box = ax.get_position()
+ ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
+ ax.legend(loc = 'upper center', ncol=num_axis, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1))
+ pylab.savefig("%s/plot_power_gpu.png" % out_dir, format="png", dpi=200)
+ print >>output, FIGURE % {'img': 'plot_power_gpu.png',
+ 'title': 'Power utilization co-related to all GPU rings'
+ }
+
+ # per-ring power
+ for ring in ["render", "bitstr", "bitstr2", "blitter"]:
+ if results["%s_avg" % ring] == -1:
+ print "No data for %s, skipping" % ring
+ continue
+ fig = pylab.figure()
+ ax = pylab.subplot(111)
+ pylab.title("Power usage vs %s ring" % ring)
+ pylab.ylabel("Usage (%)")
+ pylab.xlabel("Time (s)")
+
+ ax.plot(results['time'], results[ring], label=ring)
+ # Do we have power?
+ # plotting power
+ ax2 = ax.twinx()
+ ax2.plot(results['time'], results["power"], label="Power (W)", color='red')
+ ax2.set_ylabel('Watts')
+
+ pylab.grid()
+ # Shink current axis's height by 10% on the bottom
+ box = ax.get_position()
+ ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
+ ax.legend(loc = 'upper center', ncol=2, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1))
+ pylab.savefig("%s/plot_power_gpu_%s.png" % (out_dir, ring), format="png", dpi=200)
+ print >>output, FIGURE % {'img': 'plot_power_gpu_%s.png' % ring,
+ 'title': 'Power utilization co-related to the %s ring' % ring
+ }
+
+ pass
+
+ print >>output, TAIL
+
+def usage(cmd):
+ """Prints usage message"""
+ print """Intel-gpu-analyser: intel GPU data analyser
+
+Usage: %s [parameters].
+
+The following arguments are accepted:
+ -h, --help display help message
+ -l, --logfile intel-gpu-top log file to analyse
+ -c, --command command to profile
+ -t, --title description of the command analysed
+ -o, --output output directory for the results
+
+You need to specify either a log file to analyse, or a command to profile.
+When you specify both -l and -c, the last one takes predence.
+
+If you do not give a log file to analyse, or a command to profile, this
+tool will become sad, depressed and suicide itself with exit code of -1, and
+you will feel bad about it.
+"""
if __name__ == "__main__":
- if len(sys.argv) < 2:
- print "Usage: %s <command to profile>" % sys.argv[0]
+ # parse command line
+ try:
+ opt, args = getopt.getopt(sys.argv[1:], 'hl:c:t:o:', ['help', 'logfile=', 'command=', 'title=', 'output='])
+ except getopt.error:
+ usage(sys.argv[0])
+ sys.exit(1)
+ logfile = None
+ title = None
+ output = os.curdir
+ for o in opt:
+ # help
+ if o[0] == '-h' or o[0] == '--help':
+ usage(sys.argv[0])
+ sys.exit(0)
+ # list
+ elif o[0] == '-l' or o[0] == '--logfile':
+ logfile = open(o[1], "r")
+ title = "Log file '%s'" % o[1]
+ elif o[0] == '-c' or o[0] == '--command':
+ logfile = os.popen("intel_gpu_top -o - -e \"%s\"" % o[1])
+ title = "Execution of '%s'" % o[1]
+ # force new level
+ elif o[0] == '-t' or o[0] == '--title':
+ title = o[1]
+ elif o[0] == '-o' or o[0] == '--output':
+ output = o[1]
+ if not logfile:
+ usage(sys.argv[0])
+ print "Error: no log file and no command to profile, don't know what to do"
sys.exit(1)
- results = collect(sys.argv[1])
- analyse(results)
- print results
+ try:
+ os.makedirs(output)
+ except:
+ pass
+ results = collect(logfile)
+ analyse(results, title, output)