source: benchmark/rmit.py@ 753fb978

ADT ast-experimental enum forall-pointer-decay pthread-emulation qualifiedEnum
Last change on this file since 753fb978 was 83b22b53, checked in by Thierry Delisle <tdelisle@…>, 4 years ago

now print expected time before listing.

  • Property mode set to 100755
File size: 7.6 KB
Line 
1#!/usr/bin/python3
2"""
3Python Script to implement R.M.I.T. testing : Randomized Multiple Interleaved Trials
4
5./rmit.py run COMMAND CANDIDATES
6-t trials
7-o option:values
8"""
9
10
11import argparse
12import datetime
13import itertools
14import json
15import os
16import random
17import re
18import socket
19import subprocess
20import sys
21
22
23def parse_range(x):
24 result = []
25 for part in x.split(','):
26 if '-' in part:
27 a, b = part.split('-')
28 a, b = int(a), int(b)
29 result.extend(range(a, b + 1))
30 else:
31 a = int(part)
32 result.append(a)
33 return result
34
35class DependentOpt:
36 def __init__(self, key, value):
37 self.key = key
38 self.value = value
39 self.vars = re.findall("[a-zA-Z]", value)
40
41def parse_option(key, values):
42 try:
43 num = int(values)
44 return key, [num]
45 except:
46 pass
47
48 if re.search("^[0-9-,]+$", values):
49 values = parse_range(values)
50 return key, [v for v in values]
51 else:
52 return key, DependentOpt(key, values)
53
54def eval_one(fmt, vals):
55 orig = fmt
56 for k, v in vals:
57 fmt = fmt.replace(k, str(v))
58
59 if not re.search("^[0-9-/*+ ]+$", fmt):
60 print('ERROR: pattern option {} (interpreted as {}) could not be evaluated'.format(orig, fmt), file=sys.stderr)
61 sys.exit(1)
62
63 return eval(fmt)
64
65def eval_options(opts):
66 dependents = [d for d in opts.values() if type(d) is DependentOpt]
67 processed = []
68 nopts = []
69 for d in dependents:
70 processed.append(d.key)
71 lists = []
72 for dvar in d.vars:
73 if not dvar in opts.keys():
74 print('ERROR: extra pattern option {}:{} uses unknown key {}'.format(d.key,d.value,dvar), file=sys.stderr)
75 sys.exit(1)
76
77 lists.append([(dvar, o) for o in opts[dvar]])
78 processed.append(dvar)
79
80 kopt = []
81 for vals in list(itertools.product(*lists)):
82 res = ['-{}'.format(d.key), "{}".format(eval_one(d.value, vals))]
83 for k, v in vals:
84 res.extend(['-{}'.format(k), "{}".format(v)])
85 kopt.append(res)
86 nopts.append(kopt)
87
88
89 for k, vals in opts.items():
90 if k not in processed:
91 kopt = []
92 for v in vals:
93 kopt.append(['-{}'.format(k), "{}".format(v)])
94 nopts.append(kopt)
95
96 return nopts
97
98# returns the first option with key 'opt'
99def search_option(action, opt):
100 i = 0
101 while i < len(action):
102 if action[i] == opt:
103 i += 1
104 if i != len(action):
105 return action[i]
106 i += 1
107
108 return None
109
110def actions_eta(actions):
111 time = 0
112 for a in actions:
113 o = search_option(a, '-d')
114 if o :
115 time += int(o)
116 return time
117
118taskset_maps = None
119
120def init_taskset_maps():
121 global taskset_maps
122 known_hosts = {
123 "jax": {
124 range( 1, 24) : "48-71",
125 range( 25, 48) : "48-71,144-167",
126 range( 49, 96) : "48-95,144-191",
127 range( 97, 144) : "24-95,120-191",
128 range(145, 192) : "0-95,96-191",
129 },
130 }
131
132 if (host := socket.gethostname()) in known_hosts:
133 taskset_maps = known_hosts[host]
134 return True
135
136 print("Warning unknown host '{}', disable taskset usage".format(host), file=sys.stderr)
137 return False
138
139
140def settaskset_one(action):
141 o = search_option(action, '-p')
142 if not o:
143 return action
144 try:
145 oi = int(o)
146 except ValueError:
147 return action
148
149 m = "Not found"
150 for key in taskset_maps:
151 if oi in key:
152 return ['taskset', '-c', taskset_maps[key], *action]
153
154 print("Warning no mapping for {} cores".format(oi), file=sys.stderr)
155 return action
156
157def settaskset(actions):
158 return [settaskset_one(a) for a in actions]
159
160if __name__ == "__main__":
161 # ================================================================================
162 # parse command line arguments
163 formats = ['raw', 'csv', 'json']
164 parser = argparse.ArgumentParser(description='Python Script to implement R.M.I.T. testing : Randomized Multiple Interleaved Trials')
165 parser.add_argument('--list', help='List all the commands that would be run', action='store_true')
166 parser.add_argument('--file', nargs='?', type=argparse.FileType('w'), default=sys.stdout)
167 parser.add_argument('--trials', help='Number of trials to run per combinaison', type=int, default=3)
168 parser.add_argument('--notaskset', help='If specified, the trial will not use taskset to match the -p option', action='store_true')
169 parser.add_argument('command', metavar='command', type=str, nargs=1, help='the command prefix to run')
170 parser.add_argument('candidates', metavar='candidates', type=str, nargs='*', help='the candidate suffix to run')
171
172 try:
173 options, unknown = parser.parse_known_args()
174
175 options.option = []
176 while unknown:
177 key = unknown.pop(0)
178 val = unknown.pop(0)
179
180 if key[0] != '-':
181 raise ValueError
182
183 options.option.append((key[1:], val))
184
185 except:
186 print('ERROR: invalid arguments', file=sys.stderr)
187 parser.print_help(sys.stderr)
188 sys.exit(1)
189
190 # ================================================================================
191 # Identify the commands to run
192 command = './' + options.command[0]
193 if options.candidates:
194 commands = [command + "-" + c for c in options.candidates]
195 else:
196 commands = [command]
197 for c in commands:
198 if not os.path.isfile(c):
199 print('ERROR: invalid command {}, file does not exist'.format(c), file=sys.stderr)
200 sys.exit(1)
201
202 if not os.access(c, os.X_OK):
203 print('ERROR: invalid command {}, file not executable'.format(c), file=sys.stderr)
204 sys.exit(1)
205
206
207 # ================================================================================
208 # Identify the options to run
209 opts = dict([parse_option(k, v) for k, v in options.option])
210
211 # Evaluate the options (options can depend on the value of other options)
212 opts = eval_options(opts)
213
214 # ================================================================================
215 # Figure out all the combinations to run
216 actions = []
217 for p in itertools.product(range(options.trials), commands, *opts):
218 act = [p[1]]
219 for o in p[2:]:
220 act.extend(o)
221 actions.append(act)
222
223 # ================================================================================
224 # Fixup the different commands
225
226 # Add tasksets
227 withtaskset = False
228 if not options.notaskset and init_taskset_maps():
229 withtaskset = True
230 actions = settaskset(actions)
231
232 # ================================================================================
233 # Now that we know what to run, print it.
234 # find expected time
235 time = actions_eta(actions)
236 print("Running {} trials{}".format(len(actions), "" if time == 0 else " (expecting to take {})".format(str(datetime.timedelta(seconds=int(time)))) ))
237
238 # dry run if options ask for it
239 if options.list:
240 for a in actions:
241 print(" ".join(a))
242 sys.exit(0)
243
244
245 # ================================================================================
246 # Prepare to run
247
248 random.shuffle(actions)
249
250 # ================================================================================
251 # Run
252 options.file.write("[")
253 first = True
254 for i, a in enumerate(actions):
255 sa = " ".join(a[3:] if withtaskset else a)
256 if first:
257 first = False
258 else:
259 options.file.write(",")
260 if options.file != sys.stdout:
261 print("{}/{} : {} \r".format(i, len(actions), sa), end = '')
262 fields = {}
263 with subprocess.Popen( a, stdout = subprocess.PIPE, stderr = subprocess.PIPE) as proc:
264 out, err = proc.communicate()
265 if proc.returncode != 0:
266 print("ERROR: command '{}' encountered error, returned code {}".format(sa, proc.returncode), file=sys.stderr)
267 print(err.decode("utf-8"))
268 sys.exit(1)
269 for s in out.decode("utf-8").splitlines():
270 match = re.search("^(.*):(.*)$", s)
271 if match:
272 try:
273 fields[match.group(1).strip()] = float(match.group(2).strip().replace(',',''))
274 except:
275 pass
276
277 options.file.write(json.dumps([a[3 if withtaskset else 0][2:], sa, fields]))
278 options.file.flush()
279
280 options.file.write("]\n")
281
282 if options.file != sys.stdout:
283 print("Done ")
Note: See TracBrowser for help on using the repository browser.