diff options
| author | Björn B. Brandenburg <bbb@cs.unc.edu> | 2010-03-27 11:46:37 -0400 |
|---|---|---|
| committer | Björn B. Brandenburg <bbb@cs.unc.edu> | 2010-03-27 11:46:37 -0400 |
| commit | 137365d1ea9c736f67184b6ed0bb683326a55243 (patch) | |
| tree | de589a286e99f460c559184a21bb10c9fa4a88df | |
| parent | ebb62de88cf64ba87bcb332625cc9d75a88c7257 (diff) | |
First steps in aggregate plotting.
| -rwxr-xr-x | plot_pm2.py | 139 |
1 files changed, 130 insertions, 9 deletions
diff --git a/plot_pm2.py b/plot_pm2.py index 866b4b2..3c0174d 100755 --- a/plot_pm2.py +++ b/plot_pm2.py | |||
| @@ -3,6 +3,8 @@ from os.path import splitext, basename | |||
| 3 | from optparse import make_option as o | 3 | from optparse import make_option as o |
| 4 | from tempfile import NamedTemporaryFile as Tmp | 4 | from tempfile import NamedTemporaryFile as Tmp |
| 5 | 5 | ||
| 6 | from collections import defaultdict | ||
| 7 | |||
| 6 | import numpy as np | 8 | import numpy as np |
| 7 | from util import load_csv_file, select | 9 | from util import load_csv_file, select |
| 8 | 10 | ||
| @@ -61,6 +63,19 @@ PMO_SUBPLOTS = [ | |||
| 61 | (3, 10, 9, True), | 63 | (3, 10, 9, True), |
| 62 | ] | 64 | ] |
| 63 | 65 | ||
| 66 | PMO_AGGR_SUBPLOTS = [ | ||
| 67 | # x, y, y-delta, split according to mem-hierarchy? | ||
| 68 | (0, 6, None, False), | ||
| 69 | (0, 7, None, False), | ||
| 70 | (0, 8, None, False), | ||
| 71 | (0, 9, None, False), | ||
| 72 | (0, 10, None, True), | ||
| 73 | (0, 10, 6, True), | ||
| 74 | (0, 10, 7, True), | ||
| 75 | (0, 10, 9, True), | ||
| 76 | (0, 10, 8, True), | ||
| 77 | ] | ||
| 78 | |||
| 64 | PMO_COL_LABEL = [('measurement', 'sample', 'index'), | 79 | PMO_COL_LABEL = [('measurement', 'sample', 'index'), |
| 65 | ('write cycles', 'wcycle', 'every nth access'), | 80 | ('write cycles', 'wcycle', 'every nth access'), |
| 66 | ('WSS', 'wcc', 'kilobytes'), | 81 | ('WSS', 'wcc', 'kilobytes'), |
| @@ -84,6 +99,7 @@ options = [ | |||
| 84 | o(None, '--wide', action='store_true', dest='wide'), | 99 | o(None, '--wide', action='store_true', dest='wide'), |
| 85 | o(None, '--split', action='store_true', dest='split'), | 100 | o(None, '--split', action='store_true', dest='split'), |
| 86 | o(None, '--extend', action='store', type='float', dest='extend'), | 101 | o(None, '--extend', action='store', type='float', dest='extend'), |
| 102 | o(None, '--aggregate', action='store_true', dest='aggregate'), | ||
| 87 | ] | 103 | ] |
| 88 | 104 | ||
| 89 | defaults = { | 105 | defaults = { |
| @@ -91,6 +107,7 @@ defaults = { | |||
| 91 | 'paper' : False, | 107 | 'paper' : False, |
| 92 | 'split' : False, | 108 | 'split' : False, |
| 93 | 'wide' : False, | 109 | 'wide' : False, |
| 110 | 'aggregate' : False, | ||
| 94 | 'extend' : 1.5, | 111 | 'extend' : 1.5, |
| 95 | } | 112 | } |
| 96 | 113 | ||
| @@ -105,37 +122,94 @@ def extract_cols(data, xcol, ycol1, ycol2, cast=int, cpu_filter=lambda x, y: Tru | |||
| 105 | class CyclePlotter(defapp.App): | 122 | class CyclePlotter(defapp.App): |
| 106 | def __init__(self): | 123 | def __init__(self): |
| 107 | defapp.App.__init__(self, options, defaults, no_std_opts=True) | 124 | defapp.App.__init__(self, options, defaults, no_std_opts=True) |
| 125 | self.aggregate_data = [] | ||
| 108 | 126 | ||
| 109 | def setup_pmo_graphs(self, datafile, conf): | 127 | def setup_pmo_graphs(self, datafile, conf, subplots=PMO_SUBPLOTS): |
| 110 | host = conf['host'] | 128 | host = conf['host'] |
| 111 | if host in MACHINE_TOPOLOGY: | 129 | if host in MACHINE_TOPOLOGY: |
| 112 | (cpus, hier) = MACHINE_TOPOLOGY[host] | 130 | (cpus, hier) = MACHINE_TOPOLOGY[host] |
| 113 | plots = [] | 131 | plots = [] |
| 114 | data = load_csv_file(datafile, dtype=int) | 132 | data = load_csv_file(datafile, dtype=int) |
| 115 | for (xcol, ycol, yminus, by_mem_hierarchy) in PMO_SUBPLOTS: | 133 | for (xcol, ycol, yminus, by_mem_hierarchy) in subplots: |
| 116 | sub = [('all', lambda x, y: True)] | 134 | sub = [('all', lambda x, y: True)] |
| 117 | if by_mem_hierarchy: | 135 | if by_mem_hierarchy: |
| 118 | sub += hier | 136 | sub += hier |
| 119 | for tag, test in sub: | 137 | for tag, test in sub: |
| 120 | tmp = Tmp() | ||
| 121 | rows = extract_cols(data, | 138 | rows = extract_cols(data, |
| 122 | xcol, ycol, yminus, | 139 | xcol, ycol, yminus, |
| 123 | cpu_filter=test) | 140 | cpu_filter=test) |
| 124 | for row in rows: | 141 | plots.append((rows, xcol, ycol, yminus, tag)) |
| 125 | tmp.write("%s, %s\n" % (row[0], row[1])) | ||
| 126 | tmp.flush() | ||
| 127 | plots.append((tmp, xcol, ycol, yminus, tag, rows)) | ||
| 128 | return plots | 142 | return plots |
| 129 | else: | 143 | else: |
| 130 | self.err('Unkown host: %s' % host) | 144 | self.err('Unkown host: %s' % host) |
| 131 | return None | 145 | return None |
| 132 | 146 | ||
| 147 | def write_aggregate(self, datafiles): | ||
| 148 | # (wss, avg, wc, #avg, #wc) | ||
| 149 | # by tag -> by wcycle -> list of data points) | ||
| 150 | by_tag = defaultdict(lambda: defaultdict(list)) | ||
| 151 | |||
| 152 | for i, datafile in enumerate(datafiles): | ||
| 153 | print '[%d/%d] Processing %s...' % (i + 1, len(datafiles), datafile) | ||
| 154 | bname = basename(datafile) | ||
| 155 | name, ext = splitext(bname) | ||
| 156 | if ext != '.csv': | ||
| 157 | self.err("Warning: '%s' doesn't look like a CSV file." | ||
| 158 | % bname) | ||
| 159 | conf = decode(name) | ||
| 160 | if 'pmo' in conf: | ||
| 161 | plots = self.setup_pmo_graphs(datafile, conf, PMO_AGGR_SUBPLOTS) | ||
| 162 | if plots is None: | ||
| 163 | print "Skipping %s..." % datafile | ||
| 164 | return | ||
| 165 | wss = int(conf['wss']) | ||
| 166 | wcycle = int(conf['wcycle']) | ||
| 167 | host = conf['host'] | ||
| 168 | for (rows, xcol, ycol, yminus, tag) in plots: | ||
| 169 | clean = stats.iqr_remove_outliers(rows, extend=self.options.extend) | ||
| 170 | vals = clean[:,1] | ||
| 171 | avg = np.mean(vals) | ||
| 172 | std = np.std(vals, ddof=1) | ||
| 173 | wc = np.max(vals) | ||
| 174 | n = len(vals) | ||
| 175 | |||
| 176 | xtag = PMO_COL_LABEL[xcol][1] | ||
| 177 | ytag = PMO_COL_LABEL[ycol][1] | ||
| 178 | dtag = "-delta-%s" % PMO_COL_LABEL[yminus][1] if not yminus is None else "" | ||
| 179 | code = "code=%s-%s-%s-%s" % \ | ||
| 180 | (xcol, ycol, yminus, tag) | ||
| 181 | figname = "host=%s_%s%s-vs-%s_%s_%s" % \ | ||
| 182 | (host, ytag, dtag, xtag, tag, code) | ||
| 183 | by_tag[figname][wcycle].append((wss, avg, std, wc, n, len(rows) - n)) | ||
| 184 | del plots | ||
| 185 | else: | ||
| 186 | self.err("Warning: '%s' is not a PMO experiment; skipping." % bname) | ||
| 187 | |||
| 188 | for figname in by_tag: | ||
| 189 | for wcycle in by_tag[figname]: | ||
| 190 | data = by_tag[figname][wcycle] | ||
| 191 | # sort by increasing WSS | ||
| 192 | data.sort(key=lambda row: row[0]) | ||
| 193 | f = open('pmo-aggr_wcycle=%d_%s.csv' % (wcycle, figname), 'w') | ||
| 194 | for row in data: | ||
| 195 | f.write(", ".join([str(x) for x in row])) | ||
| 196 | f.write('\n') | ||
| 197 | f.close() | ||
| 198 | |||
| 133 | def plot_preempt_migrate(self, datafile, name, conf): | 199 | def plot_preempt_migrate(self, datafile, name, conf): |
| 134 | plots = self.setup_pmo_graphs(datafile, conf) | 200 | plots = self.setup_pmo_graphs(datafile, conf) |
| 135 | if plots is None: | 201 | if plots is None: |
| 136 | print "Skipping %s..." % datafile | 202 | print "Skipping %s..." % datafile |
| 137 | return | 203 | return |
| 138 | for (tmp, xcol, ycol, yminus, tag, rows) in plots: | 204 | else: |
| 205 | print 'Plotting %s...' % datafile | ||
| 206 | for (rows, xcol, ycol, yminus, tag) in plots: | ||
| 207 | # Write it to a temp file. | ||
| 208 | tmp = Tmp() | ||
| 209 | for row in rows: | ||
| 210 | tmp.write("%s, %s\n" % (row[0], row[1])) | ||
| 211 | tmp.flush() | ||
| 212 | |||
| 139 | xtag = PMO_COL_LABEL[xcol][1] | 213 | xtag = PMO_COL_LABEL[xcol][1] |
| 140 | ytag = PMO_COL_LABEL[ycol][1] | 214 | ytag = PMO_COL_LABEL[ycol][1] |
| 141 | dtag = "-delta-%s" % PMO_COL_LABEL[yminus][1] if not yminus is None else "" | 215 | dtag = "-delta-%s" % PMO_COL_LABEL[yminus][1] if not yminus is None else "" |
| @@ -147,7 +221,6 @@ class CyclePlotter(defapp.App): | |||
| 147 | title = "%s" % ylabel | 221 | title = "%s" % ylabel |
| 148 | if ycol == 10: | 222 | if ycol == 10: |
| 149 | title += " from %s" % PMO_MEM[tag] | 223 | title += " from %s" % PMO_MEM[tag] |
| 150 | title += "\\n" | ||
| 151 | for key in conf: | 224 | for key in conf: |
| 152 | if key in PMO_PARAM: | 225 | if key in PMO_PARAM: |
| 153 | title += " %s=%s" % (PMO_PARAM[key], conf[key]) | 226 | title += " %s=%s" % (PMO_PARAM[key], conf[key]) |
| @@ -169,6 +242,49 @@ class CyclePlotter(defapp.App): | |||
| 169 | style='points', | 242 | style='points', |
| 170 | format=self.options.format, | 243 | format=self.options.format, |
| 171 | fname=figname) | 244 | fname=figname) |
| 245 | del tmp # delete temporary file | ||
| 246 | |||
| 247 | def plot_pmo_aggr(self, datafile, name, conf): | ||
| 248 | fname = datafile | ||
| 249 | code = conf['code'] | ||
| 250 | (xcol, ycol, yminus, tag) = code.split('-') | ||
| 251 | |||
| 252 | xcol = int(xcol) | ||
| 253 | ycol = int(ycol) | ||
| 254 | if yminus != "None": | ||
| 255 | yminus = int(ycol) | ||
| 256 | else: | ||
| 257 | yminus = None | ||
| 258 | |||
| 259 | xtag = PMO_COL_LABEL[xcol][1] | ||
| 260 | ytag = PMO_COL_LABEL[ycol][1] | ||
| 261 | dtag = "-delta-%s" % PMO_COL_LABEL[yminus][1] if not yminus is None else "" | ||
| 262 | figname = "%s_%s%s-vs-%s_%s" % (name, ytag, dtag, xtag, tag) | ||
| 263 | xunit = PMO_COL_LABEL[xcol][2] | ||
| 264 | yunit = PMO_COL_LABEL[ycol][2] | ||
| 265 | ylabel = PMO_COL_LABEL[ycol][0] | ||
| 266 | xlabel = PMO_COL_LABEL[xcol][0] | ||
| 267 | title = "%s" % ylabel | ||
| 268 | |||
| 269 | ylabel="%s (%s)" % ("access cost" if yminus is None | ||
| 270 | else "delta to %s" % PMO_COL_LABEL[yminus][0], | ||
| 271 | yunit), | ||
| 272 | if ycol == 10: | ||
| 273 | title += " from %s" % PMO_MEM[tag] | ||
| 274 | for key in conf: | ||
| 275 | if key in PMO_PARAM: | ||
| 276 | title += " %s=%s" % (PMO_PARAM[key], conf[key]) | ||
| 277 | |||
| 278 | graphs = [ | ||
| 279 | #(fname, 1, 2, "average"), | ||
| 280 | "'%s' using 1:2:3 title 'average' with errorbars" % (fname), | ||
| 281 | (fname, 1, 4, "maximum"), | ||
| 282 | ] | ||
| 283 | xlabel = "working set size (kilobytes)" | ||
| 284 | |||
| 285 | gnuplot(graphs, xlabel=xlabel, ylabel=ylabel, title=title, fname=figname, | ||
| 286 | logscale="xy 2" if yminus is None else "x 2", | ||
| 287 | format=self.options.format) | ||
| 172 | 288 | ||
| 173 | def plot_file(self, datafile): | 289 | def plot_file(self, datafile): |
| 174 | bname = basename(datafile) | 290 | bname = basename(datafile) |
| @@ -179,6 +295,8 @@ class CyclePlotter(defapp.App): | |||
| 179 | conf = decode(name) | 295 | conf = decode(name) |
| 180 | if 'pmo' in conf: | 296 | if 'pmo' in conf: |
| 181 | self.plot_preempt_migrate(datafile, name, conf) | 297 | self.plot_preempt_migrate(datafile, name, conf) |
| 298 | elif 'pmo-aggr' in conf: | ||
| 299 | self.plot_pmo_aggr(datafile, name, conf) | ||
| 182 | else: | 300 | else: |
| 183 | self.err("Skipped '%s'; unkown experiment type." | 301 | self.err("Skipped '%s'; unkown experiment type." |
| 184 | % bname) | 302 | % bname) |
| @@ -187,5 +305,8 @@ class CyclePlotter(defapp.App): | |||
| 187 | for datafile in self.args: | 305 | for datafile in self.args: |
| 188 | self.plot_file(datafile) | 306 | self.plot_file(datafile) |
| 189 | 307 | ||
| 308 | def do_aggregate(self, _): | ||
| 309 | self.write_aggregate(self.args[1:]) | ||
| 310 | |||
| 190 | if __name__ == "__main__": | 311 | if __name__ == "__main__": |
| 191 | CyclePlotter().launch() | 312 | CyclePlotter().launch() |
