aboutsummaryrefslogtreecommitdiffstats
path: root/parse_exps.py
diff options
context:
space:
mode:
Diffstat (limited to 'parse_exps.py')
-rwxr-xr-xparse_exps.py199
1 files changed, 125 insertions, 74 deletions
diff --git a/parse_exps.py b/parse_exps.py
index 9475cfc..7a99d8a 100755
--- a/parse_exps.py
+++ b/parse_exps.py
@@ -1,8 +1,9 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2from __future__ import print_function 2from __future__ import print_function
3 3
4import config.config as conf
5import copy 4import copy
5import common as com
6import multiprocessing
6import os 7import os
7import parse.ft as ft 8import parse.ft as ft
8import parse.sched as st 9import parse.sched as st
@@ -13,18 +14,19 @@ import sys
13import traceback 14import traceback
14 15
15from collections import namedtuple 16from collections import namedtuple
16from common import load_params 17from config.config import DEFAULTS,PARAMS
17from optparse import OptionParser 18from optparse import OptionParser
18from parse.point import ExpPoint 19from parse.point import ExpPoint
19from parse.tuple_table import TupleTable 20from parse.tuple_table import TupleTable
20from parse.col_map import ColMapBuilder 21from parse.col_map import ColMapBuilder
21from multiprocessing import Pool, cpu_count 22
22 23
23def parse_args(): 24def parse_args():
24 parser = OptionParser("usage: %prog [options] [data_dir]...") 25 parser = OptionParser("usage: %prog [options] [data_dir]...")
25 26
26 parser.add_option('-o', '--out', dest='out', 27 parser.add_option('-o', '--out', dest='out',
27 help='file or directory for data output', default='parse-data') 28 help='file or directory for data output',
29 default=DEFAULTS['out-parse'])
28 parser.add_option('-i', '--ignore', metavar='[PARAM...]', default="", 30 parser.add_option('-i', '--ignore', metavar='[PARAM...]', default="",
29 help='ignore changing parameter values') 31 help='ignore changing parameter values')
30 parser.add_option('-f', '--force', action='store_true', default=False, 32 parser.add_option('-f', '--force', action='store_true', default=False,
@@ -34,7 +36,8 @@ def parse_args():
34 parser.add_option('-m', '--write-map', action='store_true', default=False, 36 parser.add_option('-m', '--write-map', action='store_true', default=False,
35 dest='write_map', 37 dest='write_map',
36 help='Output map of values instead of csv tree') 38 help='Output map of values instead of csv tree')
37 parser.add_option('-p', '--processors', default=max(cpu_count() - 1, 1), 39 parser.add_option('-p', '--processors',
40 default=max(multiprocessing.cpu_count() - 1, 1),
38 type='int', dest='processors', 41 type='int', dest='processors',
39 help='number of threads for processing') 42 help='number of threads for processing')
40 parser.add_option('-s', '--scale-against', dest='scale_against', 43 parser.add_option('-s', '--scale-against', dest='scale_against',
@@ -43,12 +46,59 @@ def parse_args():
43 46
44 return parser.parse_args() 47 return parser.parse_args()
45 48
49
46ExpData = namedtuple('ExpData', ['path', 'params', 'work_dir']) 50ExpData = namedtuple('ExpData', ['path', 'params', 'work_dir'])
47 51
52
53def parse_exp(exp_force_base):
54 # Tupled for multiprocessing
55 exp, force, base_table = exp_force_base
56
57 result_file = exp.work_dir + "/exp_point.pkl"
58 should_load = not force and os.path.exists(result_file)
59
60 result = None
61 if should_load:
62 with open(result_file, 'rb') as f:
63 try:
64 # No need to go through this work twice
65 result = pickle.load(f)
66 except:
67 pass
68
69 if not result:
70 try:
71 # Create a readable name
72 name = os.path.relpath(exp.path)
73 name = name if name != "." else os.path.split(os.getcwd())[1]
74
75 result = ExpPoint(name)
76
77 # Write overheads into result
78 cycles = exp.params[PARAMS['cycles']]
79 ft.extract_ft_data(result, exp.path, exp.work_dir, cycles)
80
81 # Write scheduling statistics into result
82 st.extract_sched_data(result, exp.path, exp.work_dir)
83
84 # Write scaling factors into result
85 if base_table and exp.params in base_table:
86 base_exp = base_table[exp.params]
87 if base_exp != exp:
88 st.extract_mc_data(result, exp.path, base_exp.path)
89
90 with open(result_file, 'wb') as f:
91 pickle.dump(result, f)
92 except:
93 traceback.print_exc()
94
95 return (exp, result)
96
97
48def get_exp_params(data_dir, cm_builder): 98def get_exp_params(data_dir, cm_builder):
49 param_file = "%s/%s" % (data_dir, conf.DEFAULTS['params_file']) 99 param_file = "%s/%s" % (data_dir, DEFAULTS['params_file'])
50 if os.path.isfile(param_file): 100 if os.path.isfile(param_file):
51 params = load_params(param_file) 101 params = com.load_params(param_file)
52 102
53 # Store parameters in cm_builder, which will track which parameters change 103 # Store parameters in cm_builder, which will track which parameters change
54 # across experiments 104 # across experiments
@@ -58,8 +108,8 @@ def get_exp_params(data_dir, cm_builder):
58 params = {} 108 params = {}
59 109
60 # Cycles must be present for feather-trace measurement parsing 110 # Cycles must be present for feather-trace measurement parsing
61 if conf.PARAMS['cycles'] not in params: 111 if PARAMS['cycles'] not in params:
62 params[conf.PARAMS['cycles']] = conf.DEFAULTS['cycles'] 112 params[PARAMS['cycles']] = DEFAULTS['cycles']
63 113
64 return params 114 return params
65 115
@@ -87,44 +137,6 @@ def load_exps(exp_dirs, cm_builder, force):
87 137
88 return exps 138 return exps
89 139
90def parse_exp(exp_force_base):
91 # Tupled for multiprocessing
92 exp, force, base_table = exp_force_base
93
94 result_file = exp.work_dir + "/exp_point.pkl"
95 should_load = not force and os.path.exists(result_file)
96
97 result = None
98 if should_load:
99 with open(result_file, 'rb') as f:
100 try:
101 # No need to go through this work twice
102 result = pickle.load(f)
103 except:
104 pass
105
106 if not result:
107 try:
108 result = ExpPoint(exp.path)
109 cycles = exp.params[conf.PARAMS['cycles']]
110
111 # Write overheads into result
112 ft.extract_ft_data(result, exp.path, exp.work_dir, cycles)
113
114 # Write scheduling statistics into result
115 st.extract_sched_data(result, exp.path, exp.work_dir)
116
117 if base_table and exp.params in base_table:
118 base_exp = base_table[exp.params]
119 if base_exp != exp:
120 st.extract_scaling_data(result, exp.path, base_exp.path)
121
122 with open(result_file, 'wb') as f:
123 pickle.dump(result, f)
124 except:
125 traceback.print_exc()
126
127 return (exp, result)
128 140
129def make_base_table(cmd_scale, col_map, exps): 141def make_base_table(cmd_scale, col_map, exps):
130 if not cmd_scale: 142 if not cmd_scale:
@@ -137,8 +149,7 @@ def make_base_table(cmd_scale, col_map, exps):
137 if param not in col_map: 149 if param not in col_map:
138 raise IOError("Base column '%s' not present in any parameters!" % param) 150 raise IOError("Base column '%s' not present in any parameters!" % param)
139 151
140 base_map = copy.deepcopy(col_map) 152 base_table = TupleTable(copy.deepcopy(col_map))
141 base_table = TupleTable(base_map)
142 153
143 # Fill table with exps who we will scale against 154 # Fill table with exps who we will scale against
144 for exp in exps: 155 for exp in exps:
@@ -147,41 +158,49 @@ def make_base_table(cmd_scale, col_map, exps):
147 158
148 return base_table 159 return base_table
149 160
150def main():
151 opts, args = parse_args()
152 161
153 args = args or [os.getcwd()] 162def get_dirs(args):
154 163 if args:
155 # Load exp parameters into a ColMap 164 return args
156 builder = ColMapBuilder() 165 elif os.path.exists(DEFAULTS['out-run']):
157 exps = load_exps(args, builder, opts.force) 166 sys.stderr.write("Reading data from %s/*\n" % DEFAULTS['out-run'])
167 sched_dirs = os.listdir(DEFAULTS['out-run'])
168 return ['%s/%s' % (DEFAULTS['out-run'], d) for d in sched_dirs]
169 else:
170 sys.stderr.write("Reading data from current directory.\n")
171 return [os.getcwd()]
158 172
159 # Don't track changes in ignored parameters
160 if opts.ignore:
161 for param in opts.ignore.split(","):
162 builder.try_remove(param)
163 # Always average multiple trials
164 builder.try_remove(conf.PARAMS['trial'])
165 173
166 col_map = builder.build() 174def fill_table(table, exps, opts):
167 result_table = TupleTable(col_map) 175 sys.stderr.write("Parsing data...\n")
168 176
169 base_table = make_base_table(opts.scale_against, col_map, exps) 177 procs = min(len(exps), opts.processors)
178 logged = multiprocessing.Manager().list()
170 179
171 sys.stderr.write("Parsing data...\n") 180 sys.stderr.write("Parsing data...\n")
172 181
173 procs = min(len(exps), opts.processors) 182 base_table = make_base_table(opts.scale_against,
174 pool = Pool(processes=procs) 183 table.get_col_map(), exps)
184
185 pool = multiprocessing.Pool(processes=procs,
186 # Share a list of previously logged messages amongst processes
187 # This is for the com.log_once method to use
188 initializer=com.set_logged_list, initargs=(logged,))
189
175 pool_args = zip(exps, [opts.force]*len(exps), [base_table]*len(exps)) 190 pool_args = zip(exps, [opts.force]*len(exps), [base_table]*len(exps))
176 enum = pool.imap_unordered(parse_exp, pool_args, 1) 191 enum = pool.imap_unordered(parse_exp, pool_args, 1)
177 192
178 try: 193 try:
179 for i, (exp, result) in enumerate(enum): 194 for i, (exp, result) in enumerate(enum):
195 if not result:
196 continue
197
180 if opts.verbose: 198 if opts.verbose:
181 print(result) 199 print(result)
182 else: 200 else:
183 sys.stderr.write('\r {0:.2%}'.format(float(i)/len(exps))) 201 sys.stderr.write('\r {0:.2%}'.format(float(i)/len(exps)))
184 result_table[exp.params] += [result] 202 table[exp.params] += [result]
203
185 pool.close() 204 pool.close()
186 except: 205 except:
187 pool.terminate() 206 pool.terminate()
@@ -192,29 +211,61 @@ def main():
192 211
193 sys.stderr.write('\n') 212 sys.stderr.write('\n')
194 213
195 if opts.force and os.path.exists(opts.out):
196 sh.rmtree(opts.out)
197 214
198 reduced_table = result_table.reduce() 215def write_output(table, opts):
216 reduced_table = table.reduce()
199 217
200 if opts.write_map: 218 if opts.write_map:
201 sys.stderr.write("Writing python map into %s...\n" % opts.out) 219 sys.stderr.write("Writing python map into %s...\n" % opts.out)
202 # Write summarized results into map
203 reduced_table.write_map(opts.out) 220 reduced_table.write_map(opts.out)
204 else: 221 else:
222 if opts.force and os.path.exists(opts.out):
223 sh.rmtree(opts.out)
224
205 # Write out csv directories for all variable params 225 # Write out csv directories for all variable params
206 dir_map = reduced_table.to_dir_map() 226 dir_map = reduced_table.to_dir_map()
207 227
208 # No csvs to write, assume user meant to print out data 228 # No csvs to write, assume user meant to print out data
209 if dir_map.is_empty(): 229 if dir_map.is_empty():
210 if not opts.verbose: 230 if not opts.verbose:
211 sys.stderr.write("Too little data to make csv files.\n") 231 sys.stderr.write("Too little data to make csv files, " +
212 for key, exp in result_table: 232 "printing results.\n")
233 for key, exp in table:
213 for e in exp: 234 for e in exp:
214 print(e) 235 print(e)
215 else: 236 else:
216 sys.stderr.write("Writing csvs into %s...\n" % opts.out) 237 sys.stderr.write("Writing csvs into %s...\n" % opts.out)
217 dir_map.write(opts.out) 238 dir_map.write(opts.out)
218 239
240
241def main():
242 opts, args = parse_args()
243 exp_dirs = get_dirs(args)
244
245 # Load experiment parameters into a ColMap
246 builder = ColMapBuilder()
247 exps = load_exps(exp_dirs, builder, opts.force)
248
249 # Don't track changes in ignored parameters
250 if opts.ignore:
251 for param in opts.ignore.split(","):
252 builder.try_remove(param)
253
254 # Always average multiple trials
255 builder.try_remove(PARAMS['trial'])
256 # Only need this for feather-trace parsing
257 builder.try_remove(PARAMS['cycles'])
258
259 col_map = builder.build()
260 table = TupleTable(col_map)
261
262 fill_table(table, exps, opts)
263
264 if not table:
265 sys.stderr.write("Found no data to parse!")
266 sys.exit(1)
267
268 write_output(table, opts)
269
219if __name__ == '__main__': 270if __name__ == '__main__':
220 main() 271 main()