diff options
author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2011-06-28 20:50:08 -0400 |
---|---|---|
committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2011-06-28 20:50:08 -0400 |
commit | 07077acb1472b871a508d1b4c4f74e4e8eb6f1ae (patch) | |
tree | 5fa115e609fee6797f948cc7db30aa9a48c7cbee | |
parent | ae763c930b07ed7fe44ee2e8f02a91c267f8a297 (diff) |
keep track of script used for plotting dissertation results
-rwxr-xr-x | dplot.py | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/dplot.py b/dplot.py new file mode 100755 index 0000000..9643db6 --- /dev/null +++ b/dplot.py | |||
@@ -0,0 +1,497 @@ | |||
1 | #!/usr/bin/env python | ||
2 | import defapp | ||
3 | |||
4 | from plot import decode, get_data_tmpfile, scenario_heading | ||
5 | from util import load_csv_file, load_binary_file, write_csv_file | ||
6 | from stats import iqr_cutoff | ||
7 | |||
8 | from binary_data import get_data | ||
9 | |||
10 | from math import ceil | ||
11 | |||
12 | import numpy | ||
13 | import csv | ||
14 | from os.path import splitext, basename | ||
15 | from optparse import make_option as o | ||
16 | |||
17 | from gnuplot import gnuplot, FORMATS, Plot, label, curve | ||
18 | |||
19 | options = [ | ||
20 | # output options | ||
21 | o('-f', '--format', action='store', dest='format', type='choice', | ||
22 | choices=FORMATS, help='output format'), | ||
23 | o(None, '--save-script', action='store_true', dest='save_script'), | ||
24 | o('-p', '--prefix', action='store', dest='prefix'), | ||
25 | |||
26 | o('-x', '--xmax', action='store', dest='xmax', type='int', | ||
27 | help='determines x-axis range'), | ||
28 | |||
29 | o('-y', '--ymax', action='store', dest='ymax', type='float', | ||
30 | help='determines y-axis range'), | ||
31 | |||
32 | o(None, '--ylog', action='store_true', dest='ylog', | ||
33 | help='use logarithmic y-axis'), | ||
34 | |||
35 | |||
36 | o(None, '--pd2-only', action='store_true', dest='pd2_only'), | ||
37 | o(None, '--edf-only', action='store_true', dest='edf_only'), | ||
38 | o(None, '--ed-only', action='store_true', dest='ed_only'), | ||
39 | |||
40 | |||
41 | o('-s', '--sched', action='append', dest='schedulers'), | ||
42 | |||
43 | o(None, '--cpmd', action='append', dest='cpmd', | ||
44 | help='which CPMD cost to use'), | ||
45 | |||
46 | o('-m', '--max-tardiness', action='store_true', dest='max_tard', | ||
47 | help='plot maximum and not average tardiness'), | ||
48 | |||
49 | o(None, '--slides', action='store_true', dest='slides'), | ||
50 | o(None, '--smooth', action='store_true', dest='smooth'), | ||
51 | ] | ||
52 | |||
53 | defaults = { | ||
54 | # output options | ||
55 | 'format' : 'pdf', | ||
56 | 'save_script' : False, | ||
57 | 'prefix' : '', | ||
58 | |||
59 | 'slides' : False, | ||
60 | 'smooth' : False, | ||
61 | 'lines' : True, | ||
62 | |||
63 | 'schedulers' : None, | ||
64 | 'pd2_only' : False, | ||
65 | 'edf_only' : False, | ||
66 | 'ed_only' : False, | ||
67 | |||
68 | 'max_tard' : False, | ||
69 | |||
70 | 'cpmd' : None, | ||
71 | |||
72 | |||
73 | 'xmax' : None, | ||
74 | 'ymax' : None, | ||
75 | 'ylog' : False, | ||
76 | } | ||
77 | |||
78 | HOST_CPUS = { | ||
79 | 'ludwig' : 24, | ||
80 | } | ||
81 | |||
82 | HOST_WSS = { | ||
83 | 'ludwig' : 3072 | ||
84 | } | ||
85 | |||
86 | SCHEDULERS = [ | ||
87 | 'C-EDF-L2', | ||
88 | 'C-EDF-L2-RM', | ||
89 | 'C-EDF-L3', | ||
90 | 'C-EDF-L3-RM', | ||
91 | 'G-EDF', | ||
92 | 'G-EDF-RM', | ||
93 | 'P-EDF', | ||
94 | 'P-EDF-RM', | ||
95 | 'P-FP', | ||
96 | 'P-FP-RM', | ||
97 | 'PD2', | ||
98 | 'PD2-L2', | ||
99 | 'PD2-L2-RM', | ||
100 | 'PD2-L3', | ||
101 | 'PD2-L3-RM', | ||
102 | 'PD2-RM', | ||
103 | 'S-PD2', | ||
104 | 'S-PD2-L2', | ||
105 | 'S-PD2-L2-RM', | ||
106 | 'S-PD2-L3', | ||
107 | 'S-PD2-L3-RM', | ||
108 | 'S-PD2-RM', | ||
109 | ] | ||
110 | |||
111 | SCHEDPOS = {} | ||
112 | for (i, s) in enumerate(SCHEDULERS): | ||
113 | SCHEDPOS[s] = i | ||
114 | |||
115 | |||
116 | DIST_NAME = { | ||
117 | 'bimo-heavy' : '', | ||
118 | 'bimo-heavy-250' : '', | ||
119 | 'bimo-heavy-33' : '', | ||
120 | 'bimo-light' : '', | ||
121 | 'bimo-light-250' : '', | ||
122 | 'bimo-light-33' : '', | ||
123 | 'bimo-medium' : '', | ||
124 | 'bimo-medium-250' : '', | ||
125 | 'bimo-medium-33' : '', | ||
126 | 'exp-10-10-100' : '', | ||
127 | 'exp-25-10-100' : '', | ||
128 | 'exp-50-10-100' : '', | ||
129 | 'uni-heavy' : 'utilization uniformly in []', | ||
130 | 'uni-heavy-250' : '', | ||
131 | 'uni-heavy-33' : '', | ||
132 | 'uni-light' : '', | ||
133 | 'uni-light-250' : '', | ||
134 | 'uni-light-33' : '', | ||
135 | 'uni-medium' : '', | ||
136 | 'uni-medium-250' : '', | ||
137 | 'uni-medium-33' : '', | ||
138 | } | ||
139 | |||
140 | def sched_name(alg): | ||
141 | staggered = alg.startswith('S-') | ||
142 | dedicated = alg.endswith('-RM') | ||
143 | l3 = 'L3' in alg | ||
144 | l2 = 'L2' in alg | ||
145 | l1 = alg.startswith('P-') | ||
146 | edf = 'EDF' in alg | ||
147 | pd2 = 'PD2' in alg | ||
148 | fp = 'FP' in alg | ||
149 | |||
150 | if l3: | ||
151 | clust = 'C6-' | ||
152 | elif l2: | ||
153 | clust = 'C2-' | ||
154 | elif l1: | ||
155 | clust = 'P-' | ||
156 | else: | ||
157 | clust = 'G-' | ||
158 | |||
159 | if edf: | ||
160 | policy = 'EDF' | ||
161 | elif fp: | ||
162 | policy = 'FP' | ||
163 | elif pd2: | ||
164 | if staggered: | ||
165 | policy = 'PD2s' | ||
166 | else: | ||
167 | policy = 'PD2a' | ||
168 | else: | ||
169 | policy = '???' | ||
170 | |||
171 | if dedicated: | ||
172 | irq = '-R1' | ||
173 | else: | ||
174 | irq = '-Rm' | ||
175 | |||
176 | return clust + policy + irq | ||
177 | |||
178 | class DissPlotter(defapp.App): | ||
179 | def __init__(self): | ||
180 | defapp.App.__init__(self, options, defaults, no_std_opts=True) | ||
181 | self.tmpfiles = [] | ||
182 | |||
183 | def make_plot(self, fname=None): | ||
184 | p = Plot() | ||
185 | p.output = "%s%s.%s" % (self.options.prefix, fname, self.options.format) | ||
186 | p.format = self.options.format | ||
187 | return p | ||
188 | |||
189 | def setup_png(self, plot): | ||
190 | # standard png options; usually correct; never tweaked for paper | ||
191 | if self.options.format == 'png': | ||
192 | plot.font_size = 'large' | ||
193 | plot.size = (1024, 768) | ||
194 | plot.xticks = (0, 1) | ||
195 | plot.yticks = (0, 0.1) | ||
196 | plot.default_style = "linespoints" | ||
197 | return True | ||
198 | else: | ||
199 | return False | ||
200 | |||
201 | def write(self, data, name, ext='data'): | ||
202 | if self.options.save_script: | ||
203 | fname = "%s.%s" % (name, ext) | ||
204 | write_csv_file(fname, data) | ||
205 | return fname | ||
206 | else: | ||
207 | tmp = write_csv_file(None, data) | ||
208 | # keep a reference so that it isn't deleted | ||
209 | self.tmpfiles.append(tmp) | ||
210 | return tmp.name | ||
211 | |||
212 | def prepare(self, name, fname): | ||
213 | data = load_csv_file(fname) | ||
214 | return self.write(data, name) | ||
215 | |||
216 | def render(self, p): | ||
217 | if self.options.save_script: | ||
218 | p.gnuplot_save(p.output + '.plot') | ||
219 | else: | ||
220 | p.gnuplot_exec() | ||
221 | |||
222 | def diss_title(self, p, conf): | ||
223 | p.title = scenario_heading(conf, True) | ||
224 | |||
225 | if 'key' in conf: | ||
226 | p.title += '; WSS=%sKB ' % conf['key'] | ||
227 | |||
228 | if len(self.options.cpmd) == 1: | ||
229 | p.title += '; %s CPMD' % self.options.cpmd[0] | ||
230 | |||
231 | |||
232 | def diss_style(self, p, top_left=False): | ||
233 | if self.options.lines: | ||
234 | marker = 'lines' | ||
235 | else: | ||
236 | marger = 'linespoints' | ||
237 | |||
238 | if not self.setup_png(p): | ||
239 | p.rounded_caps = True | ||
240 | p.font = 'Helvetica' | ||
241 | |||
242 | |||
243 | if len(p.curves) > 2: | ||
244 | p.curves[2].style = marker + " ls 4" | ||
245 | |||
246 | if len(p.curves) > 3: | ||
247 | p.curves[3].style = marker + " ls 6" | ||
248 | |||
249 | if False: | ||
250 | for i, c in enumerate(p.curves): | ||
251 | c.style = "linespoints ls %d" % (i + 1) | ||
252 | |||
253 | p.line_styles = [ | ||
254 | (1, 'lw 8 lc rgbcolor "#000000"'), | ||
255 | (2, 'lw 8 lc rgbcolor "#ff0000"'), | ||
256 | (3, 'lw 8 lc rgbcolor "#0000ff"'), | ||
257 | (4, 'lw 8 lc rgbcolor "#ff910d"'), | ||
258 | # (5, 'lw 2.5 lc rgbcolor "#ff910d"'), | ||
259 | (6, "lw 2.5"), | ||
260 | (7, 'lw 2.5 lc rgbcolor "#000000"'), | ||
261 | (8, "lw 2.5"), | ||
262 | ] | ||
263 | |||
264 | |||
265 | p.font_size = '7' | ||
266 | p.size = ('6in', '2.50in') | ||
267 | p.monochrome = True #False | ||
268 | p.dashed_lines = True #True | ||
269 | if top_left: | ||
270 | p.key = 'top left' | ||
271 | else: | ||
272 | p.key = 'top right' | ||
273 | p.default_style = marker + ' lw 1' | ||
274 | p.pointsize = 2 | ||
275 | |||
276 | if self.options.slides: | ||
277 | p.dashed_lines = False | ||
278 | p.monochrome = False | ||
279 | p.rounded_caps = True | ||
280 | p.default_style = 'lines lw 10' | ||
281 | p.key = 'below' | ||
282 | |||
283 | if self.options.smooth: | ||
284 | p.default_style += " smooth bezier" | ||
285 | |||
286 | |||
287 | def plot_wsched(self, datafile, name, conf): | ||
288 | tmpfile = self.prepare(name, datafile) | ||
289 | |||
290 | p = self.make_plot(name) | ||
291 | |||
292 | if len(self.options.cpmd) > 1: | ||
293 | fmts = ['%s (load)', '%s (idle)'] | ||
294 | else: | ||
295 | fmts = ['%s', '%s'] | ||
296 | |||
297 | for sched in self.options.schedulers: | ||
298 | idx = SCHEDPOS[sched] | ||
299 | |||
300 | if 'load' in self.options.cpmd: | ||
301 | p.curves += [ | ||
302 | curve(fname=tmpfile, xcol=1, ycol=2 + (idx * 2) + 1, | ||
303 | title=fmts[0] % sched_name(sched)) | ||
304 | ] | ||
305 | |||
306 | if 'idle' in self.options.cpmd: | ||
307 | p.curves += [ | ||
308 | curve(fname=tmpfile, xcol=1, ycol=2 + (idx * 2) + 2, | ||
309 | title=fmts[1] % sched_name(sched)), | ||
310 | ] | ||
311 | |||
312 | self.diss_title(p, conf) | ||
313 | p.xlabel = "working set size (WSS) in KB" | ||
314 | p.ylabel = "weighted schedulability score" | ||
315 | |||
316 | |||
317 | if 'hard' in conf: | ||
318 | p.ylabel += " [hard]" | ||
319 | else: | ||
320 | p.ylabel += " [soft]" | ||
321 | |||
322 | if self.options.xmax: | ||
323 | p.xrange = (0, self.options.xmax) | ||
324 | elif 'host' in conf and conf['host'] in HOST_WSS: | ||
325 | p.xrange = (0, HOST_WSS[conf['host']] + 1) | ||
326 | |||
327 | p.xticks = (0, 64) | ||
328 | |||
329 | if self.options.ymax: | ||
330 | p.yrange = (-0.05, self.options.ymax) | ||
331 | else: | ||
332 | p.yrange = (-0.05, 1.05) | ||
333 | |||
334 | p.yticks = (0, 0.1) | ||
335 | |||
336 | self.diss_style(p) | ||
337 | |||
338 | self.render(p) | ||
339 | |||
340 | |||
341 | def plot_sched(self, datafile, name, conf): | ||
342 | |||
343 | tmpfile = self.prepare(name, datafile) | ||
344 | |||
345 | p = self.make_plot(name) | ||
346 | |||
347 | for sched in self.options.schedulers: | ||
348 | idx = SCHEDPOS[sched] | ||
349 | |||
350 | if len(self.options.cpmd) > 1: | ||
351 | fmts = ['%s (load)', '%s (idle)'] | ||
352 | else: | ||
353 | fmts = ['%s', '%s'] | ||
354 | |||
355 | if 'load' in self.options.cpmd: | ||
356 | p.curves += [ | ||
357 | curve(fname=tmpfile, xcol=2, ycol=2 + (idx * 2) + 1, | ||
358 | title=fmts[0] % sched_name(sched)) | ||
359 | ] | ||
360 | |||
361 | if 'idle' in self.options.cpmd: | ||
362 | p.curves += [ | ||
363 | curve(fname=tmpfile, xcol=2, ycol=2 + (idx * 2) + 2, | ||
364 | title=fmts[1] % sched_name(sched)), | ||
365 | ] | ||
366 | |||
367 | self.diss_title(p, conf) | ||
368 | p.xlabel = "utilization cap (prior to overhead accounting)" | ||
369 | p.ylabel = "schedulability" | ||
370 | |||
371 | if 'hard' in conf: | ||
372 | p.ylabel += " [hard]" | ||
373 | else: | ||
374 | p.ylabel += " [soft]" | ||
375 | |||
376 | # if self.options.xmax: | ||
377 | # p.xrange = (0.5, self.options.xmax) | ||
378 | if 'host' in conf and conf['host'] in HOST_CPUS: | ||
379 | p.xrange = (0.5, HOST_CPUS[conf['host']] + 0.5) | ||
380 | |||
381 | p.xticks = (0, 2) | ||
382 | |||
383 | if self.options.ymax: | ||
384 | p.yrange = (-0.05, self.options.ymax) | ||
385 | else: | ||
386 | p.yrange = (-0.05, 1.05) | ||
387 | |||
388 | self.diss_style(p) | ||
389 | |||
390 | self.render(p) | ||
391 | |||
392 | def plot_tardiness(self, datafile, name, conf): | ||
393 | tmpfile = self.prepare(name, datafile) | ||
394 | |||
395 | p = self.make_plot(name) | ||
396 | |||
397 | if self.options.max_tard: | ||
398 | offset = [1, 4] # use MAX column | ||
399 | tag = 'max' | ||
400 | else: | ||
401 | offset = [2, 5] # use AVG column | ||
402 | tag = 'avg' | ||
403 | |||
404 | if len(self.options.cpmd) > 1: | ||
405 | fmts = ['%s (%s, load)', '%s (%s, idle)'] | ||
406 | else: | ||
407 | fmts = ['%s (%s)', '%s (%s)'] | ||
408 | |||
409 | for sched in self.options.schedulers: | ||
410 | idx = SCHEDPOS[sched] | ||
411 | |||
412 | if 'load' in self.options.cpmd: | ||
413 | p.curves += [ | ||
414 | curve(fname=tmpfile, xcol=2, ycol=2 + (idx * 6) + offset[0], | ||
415 | title=fmts[0] % (sched_name(sched), tag)) | ||
416 | ] | ||
417 | |||
418 | if 'idle' in self.options.cpmd: | ||
419 | p.curves += [ | ||
420 | curve(fname=tmpfile, xcol=2, ycol=2 + (idx * 6) + offset[1], | ||
421 | title=fmts[1] % (sched_name(sched), tag)), | ||
422 | ] | ||
423 | |||
424 | self.diss_title(p, conf) | ||
425 | p.xlabel = "utilization cap (prior to overhead accounting)" | ||
426 | if 'rel-tard' in conf: | ||
427 | p.ylabel = "relative tardiness bound" | ||
428 | else: | ||
429 | p.ylabel = "absolute tardiness bound (in us)" | ||
430 | |||
431 | if self.options.xmax: | ||
432 | p.xrange = (0.5, self.options.xmax) | ||
433 | elif 'host' in conf and conf['host'] in HOST_CPUS: | ||
434 | p.xrange = (0.5, HOST_CPUS[conf['host']] + 0.5) | ||
435 | |||
436 | p.xticks = (0, 2) | ||
437 | |||
438 | if self.options.ymax: | ||
439 | p.yrange = (-0.05, self.options.ymax) | ||
440 | elif 'rel-tard' in conf and not self.options.max_tard: | ||
441 | p.yrange = (-0.05, 5) | ||
442 | p.yticks = (0, 0.5) | ||
443 | |||
444 | self.diss_style(p, top_left=True) | ||
445 | |||
446 | self.render(p) | ||
447 | |||
448 | |||
449 | def plot_file(self, datafile): | ||
450 | bname = basename(datafile) | ||
451 | name, ext = splitext(bname) | ||
452 | conf = decode(name) | ||
453 | plotters = { | ||
454 | 'wsched' : self.plot_wsched, | ||
455 | 'sched' : self.plot_sched, | ||
456 | 'rel-tard' : self.plot_tardiness, | ||
457 | 'abs-tard' : self.plot_tardiness, | ||
458 | } | ||
459 | |||
460 | for plot_type in plotters: | ||
461 | if plot_type in conf: | ||
462 | try: | ||
463 | plotters[plot_type](datafile, name, conf) | ||
464 | except IOError as err: | ||
465 | self.err("Skipped '%s' (%s)." % (datafile, err)) | ||
466 | break | ||
467 | else: | ||
468 | self.err("Skipped '%s'; unkown experiment type." | ||
469 | % bname) | ||
470 | # release all tmp files | ||
471 | self.tmpfiles = [] | ||
472 | |||
473 | def default(self, _): | ||
474 | if self.options.schedulers is None: | ||
475 | self.options.schedulers = SCHEDULERS | ||
476 | |||
477 | if self.options.cpmd is None: | ||
478 | self.options.cpmd = ['load', 'idle'] | ||
479 | |||
480 | if self.options.pd2_only: | ||
481 | self.options.schedulers = [s for s in self.options.schedulers | ||
482 | if 'PD2' in s] | ||
483 | |||
484 | if self.options.edf_only: | ||
485 | self.options.schedulers = [s for s in self.options.schedulers | ||
486 | if 'EDF' in s] | ||
487 | |||
488 | if self.options.ed_only: | ||
489 | self.options.schedulers = [s for s in self.options.schedulers | ||
490 | if 'EDF' in s or 'FP' in s] | ||
491 | |||
492 | for i, datafile in enumerate(self.args): | ||
493 | self.out("[%d/%d] Processing %s ..." % (i + 1, len(self.args), datafile)) | ||
494 | self.plot_file(datafile) | ||
495 | |||
496 | if __name__ == "__main__": | ||
497 | DissPlotter().launch() | ||