#!/usr/bin/env python from os.path import splitext, basename from optparse import make_option as o from tempfile import NamedTemporaryFile as Tmp import numpy as np from util import load_csv_file, select import stats import defapp from plot import decode from gnuplot import gnuplot, FORMATS MACHINE_TOPOLOGY = { 'jupiter-cs' : (4, [('preempt', lambda x, y: x == y), ('mem', lambda x, y: x != y)]), # Socket0 Socket1 Socket2 Socket3 # ------ ------- ------- ------- # | 0, 4| | 1, 5| | 2, 6| | 3, 7| # | 8,12| | 9,13| |10,14| |11,15| # |16,20| |17,21| |18,22| |19,23| # ------- ------- ------- ------- 'ludwig.cs.unc.edu' : (24, [('preempt', lambda x, y: x == y), ('l2', lambda x, y: abs(y - x) == 4), ('l3', lambda x, y: abs(y - x) > 4 and \ abs(y - x) % 4 == 0), ('mem', lambda x, y: abs(y - x) % 4 != 0)]) } PMO_PARAM = { 'wss' : 'WSS', 'host' : 'host', 'wcycle' : 'write-cycle' } PMO_MEM = { 'mem' : 'a migration through main memory', 'l3' : 'a migration through a shared L3 cache', 'l2' : 'a migration through a shared L2 cache', 'preempt' : 'a preemption', 'all' : 'either a migration or preemption', } PMO_SUBPLOTS = [ # x, y, y-delta, split according to mem-hierarchy? (0, 6, None, False), (0, 7, None, False), (0, 8, None, False), (0, 9, None, False), (0, 10, None, True), (3, 10, None, True), (0, 10, 9, True), (3, 10, 9, True), ] PMO_COL_LABEL = [('measurement', 'sample', 'index'), ('write cycles', 'wcycle', 'every nth access'), ('WSS', 'wcc', 'kilobytes'), ('suspension length', 'delay', 'microseconds'), ('CPU (preempted on)', 'from', 'processor'), ('CPU (resumed on)', 'to', 'processor'), ('cold access', 'cold', 'cycles'), ('first hot access', 'hot1', 'cycles'), ('second hot access', 'hot2', 'cycles'), ('third hot access', 'hot3', 'cycles'), ('access after resuming', 'after', 'cycles') ] PMO_FROM_CPU = 4 PMO_TO_CPU = 5 options = [ o('-f', '--format', action='store', dest='format', type='choice', choices=FORMATS, help='output format'), o(None, '--paper', action='store_true', dest='paper'), o(None, '--wide', action='store_true', dest='wide'), o(None, '--split', action='store_true', dest='split'), o(None, '--extend', action='store', type='float', dest='extend'), ] defaults = { 'format' : 'show', 'paper' : False, 'split' : False, 'wide' : False, 'extend' : 1.5, } def extract_cols(data, xcol, ycol1, ycol2, cast=int, cpu_filter=lambda x, y: True): def matching_cpus(row): return cpu_filter(row[PMO_FROM_CPU], row[PMO_TO_CPU]) rows = select(matching_cpus, data) if not (ycol2 is None): rows[:,ycol1] -= rows[:,ycol2] return rows[:,(xcol, ycol1)] class CyclePlotter(defapp.App): def __init__(self): defapp.App.__init__(self, options, defaults, no_std_opts=True) def setup_pmo_graphs(self, datafile, conf): host = conf['host'] if host in MACHINE_TOPOLOGY: (cpus, hier) = MACHINE_TOPOLOGY[host] plots = [] data = load_csv_file(datafile, dtype=int) for (xcol, ycol, yminus, by_mem_hierarchy) in PMO_SUBPLOTS: sub = [('all', lambda x, y: True)] if by_mem_hierarchy: sub += hier for tag, test in sub: tmp = Tmp() rows = extract_cols(data, xcol, ycol, yminus, cpu_filter=test) for row in rows: tmp.write("%s, %s\n" % (row[0], row[1])) tmp.flush() plots.append((tmp, xcol, ycol, yminus, tag, rows)) return plots else: self.err('Unkown host: %s' % host) return None def plot_preempt_migrate(self, datafile, name, conf): plots = self.setup_pmo_graphs(datafile, conf) if plots is None: print "Skipping %s..." % datafile return for (tmp, xcol, ycol, yminus, tag, rows) in plots: xtag = PMO_COL_LABEL[xcol][1] ytag = PMO_COL_LABEL[ycol][1] dtag = "-delta-%s" % PMO_COL_LABEL[yminus][1] if not yminus is None else "" figname = "%s_%s%s-vs-%s_%s" % (name, ytag, dtag, xtag, tag) xunit = PMO_COL_LABEL[xcol][2] yunit = PMO_COL_LABEL[ycol][2] ylabel = PMO_COL_LABEL[ycol][0] xlabel = PMO_COL_LABEL[xcol][0] title = "%s" % ylabel if ycol == 10: title += " from %s" % PMO_MEM[tag] title += "\\n" for key in conf: if key in PMO_PARAM: title += " %s=%s" % (PMO_PARAM[key], conf[key]) graphs = [(tmp.name, 1, 2, ylabel)] # plot cutoff (s, lo, hi) = stats.iqr(rows[:,1]) lo -= s * self.options.extend hi += s * self.options.extend m99 = stats.cutoff_max(rows[:, 1]) graphs += [(lo, 'IQR cutoff (%d)' % lo, 'line'), (hi, 'IQR cutoff (%d)' % hi, 'line'), (m99,'99%% cutoff (%d)' % m99, 'line lw 2')] gnuplot(graphs, xlabel="%s (%s)" % (xlabel, xunit), ylabel="%s (%s)" % ("access cost" if yminus is None else "delta to %s" % PMO_COL_LABEL[yminus][0], yunit), title=title, style='points', format=self.options.format, fname=figname) def plot_file(self, datafile): bname = basename(datafile) name, ext = splitext(bname) if ext != '.csv': self.err("Warning: '%s' doesn't look like a CSV file." % bname) conf = decode(name) if 'pmo' in conf: self.plot_preempt_migrate(datafile, name, conf) else: self.err("Skipped '%s'; unkown experiment type." % bname) def default(self, _): for datafile in self.args: self.plot_file(datafile) if __name__ == "__main__": CyclePlotter().launch()