diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-17 10:43:53 -0400 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-17 10:43:53 -0400 |
| commit | a0e4b9fe9d7fab9a50a626cfeda3c614a9a6af5d (patch) | |
| tree | c2d753ec6533dfd8be46214bc19e2b4aa044af74 /parse | |
| parent | f659a11e1c888b01cce64fee5ae064a67aa4d777 (diff) | |
Created infrastructure for calculating scaling factors.
Diffstat (limited to 'parse')
| -rw-r--r-- | parse/sched.py | 133 |
1 files changed, 108 insertions, 25 deletions
diff --git a/parse/sched.py b/parse/sched.py index 1213f0d..1f07751 100644 --- a/parse/sched.py +++ b/parse/sched.py | |||
| @@ -2,8 +2,8 @@ import config.config as conf | |||
| 2 | import os | 2 | import os |
| 3 | import re | 3 | import re |
| 4 | import struct | 4 | import struct |
| 5 | import sys | ||
| 6 | import subprocess | 5 | import subprocess |
| 6 | import sys | ||
| 7 | 7 | ||
| 8 | from collections import defaultdict,namedtuple | 8 | from collections import defaultdict,namedtuple |
| 9 | from common import recordtype | 9 | from common import recordtype |
| @@ -33,8 +33,9 @@ class TimeTracker: | |||
| 33 | self.job = record.job | 33 | self.job = record.job |
| 34 | 34 | ||
| 35 | # Data stored for each task | 35 | # Data stored for each task |
| 36 | TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu']) | 36 | TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu', 'level']) |
| 37 | TaskData = recordtype('TaskData', ['params', 'jobs', 'blocks', 'misses']) | 37 | TaskData = recordtype('TaskData', ['params', 'jobs', 'loads', |
| 38 | 'blocks', 'misses', 'execs']) | ||
| 38 | 39 | ||
| 39 | # Map of event ids to corresponding class, binary format, and processing methods | 40 | # Map of event ids to corresponding class, binary format, and processing methods |
| 40 | RecordInfo = namedtuple('RecordInfo', ['clazz', 'fmt', 'method']) | 41 | RecordInfo = namedtuple('RecordInfo', ['clazz', 'fmt', 'method']) |
| @@ -124,6 +125,7 @@ def read_data(task_dict, fnames): | |||
| 124 | 125 | ||
| 125 | def process_completion(task_dict, record): | 126 | def process_completion(task_dict, record): |
| 126 | task_dict[record.pid].misses.store_time(record) | 127 | task_dict[record.pid].misses.store_time(record) |
| 128 | task_dict[record.pid].loads += [record.load] | ||
| 127 | 129 | ||
| 128 | def process_release(task_dict, record): | 130 | def process_release(task_dict, record): |
| 129 | data = task_dict[record.pid] | 131 | data = task_dict[record.pid] |
| @@ -131,7 +133,9 @@ def process_release(task_dict, record): | |||
| 131 | data.misses.start_time(record) | 133 | data.misses.start_time(record) |
| 132 | 134 | ||
| 133 | def process_param(task_dict, record): | 135 | def process_param(task_dict, record): |
| 134 | params = TaskParams(record.wcet, record.period, record.partition) | 136 | level = chr(97 + record.level) |
| 137 | params = TaskParams(record.wcet, record.period, | ||
| 138 | record.partition, level) | ||
| 135 | task_dict[record.pid].params = params | 139 | task_dict[record.pid].params = params |
| 136 | 140 | ||
| 137 | def process_block(task_dict, record): | 141 | def process_block(task_dict, record): |
| @@ -140,14 +144,27 @@ def process_block(task_dict, record): | |||
| 140 | def process_resume(task_dict, record): | 144 | def process_resume(task_dict, record): |
| 141 | task_dict[record.pid].blocks.store_time(record) | 145 | task_dict[record.pid].blocks.store_time(record) |
| 142 | 146 | ||
| 147 | def process_switch_to(task_dict, record): | ||
| 148 | task_dict[record.pid].execs.start_time(record) | ||
| 149 | |||
| 150 | def process_switch_away(task_dict, record): | ||
| 151 | task_dict[record.pid].execs.store_time(record) | ||
| 152 | |||
| 143 | register_record('ResumeRecord', 9, process_resume, 'Q8x', ['when']) | 153 | register_record('ResumeRecord', 9, process_resume, 'Q8x', ['when']) |
| 144 | register_record('BlockRecord', 8, process_block, 'Q8x', ['when']) | 154 | register_record('BlockRecord', 8, process_block, 'Q8x', ['when']) |
| 145 | register_record('CompletionRecord', 7, process_completion, 'Q8x', ['when']) | 155 | register_record('CompletionRecord', 7, process_completion, 'QQ', ['when', 'load']) |
| 146 | register_record('ReleaseRecord', 3, process_release, 'QQ', ['release', 'when']) | 156 | register_record('ReleaseRecord', 3, process_release, 'QQ', ['release', 'when']) |
| 147 | register_record('ParamRecord', 2, process_param, 'IIIcc2x', | 157 | register_record('SwitchToRecord', 5, process_switch_to, 'Q8x', ['when']) |
| 148 | ['wcet','period','phase','partition', 'task_class']) | 158 | register_record('SwitchAwayRecord', 6, process_switch_away, 'Q8x', ['when']) |
| 159 | register_record('ParamRecord', 2, process_param, 'IIIcccx', | ||
| 160 | ['wcet','period','phase','partition', 'task_class', 'level']) | ||
| 161 | |||
| 162 | saved_stats = [] | ||
| 163 | def get_task_data(data_dir, work_dir = None): | ||
| 164 | '''Parse sched trace files''' | ||
| 165 | if data_dir in saved_stats: | ||
| 166 | return data_dir | ||
| 149 | 167 | ||
| 150 | def extract_sched_data(result, data_dir, work_dir): | ||
| 151 | bin_files = conf.FILES['sched_data'].format(".*") | 168 | bin_files = conf.FILES['sched_data'].format(".*") |
| 152 | output_file = "%s/out-st" % work_dir | 169 | output_file = "%s/out-st" % work_dir |
| 153 | 170 | ||
| @@ -157,24 +174,46 @@ def extract_sched_data(result, data_dir, work_dir): | |||
| 157 | 174 | ||
| 158 | # Save an in-english version of the data for debugging | 175 | # Save an in-english version of the data for debugging |
| 159 | # This is optional and will only be done if 'st_show' is in PATH | 176 | # This is optional and will only be done if 'st_show' is in PATH |
| 160 | if conf.BINS['st_show']: | 177 | if work_dir and conf.BINS['st_show']: |
| 161 | cmd_arr = [conf.BINS['st_show']] | 178 | cmd_arr = [conf.BINS['st_show']] |
| 162 | cmd_arr.extend(bins) | 179 | cmd_arr.extend(bins) |
| 163 | with open(output_file, "w") as f: | 180 | with open(output_file, "w") as f: |
| 164 | print("calling %s" % cmd_arr) | 181 | print("calling %s" % cmd_arr) |
| 165 | subprocess.call(cmd_arr, cwd=data_dir, stdout=f) | 182 | subprocess.call(cmd_arr, cwd=data_dir, stdout=f) |
| 166 | 183 | ||
| 167 | task_dict = defaultdict(lambda : | 184 | task_dict = defaultdict(lambda :TaskData(0, 0, 0, [], TimeTracker(), |
| 168 | TaskData(0, 0, TimeTracker(), TimeTracker())) | 185 | TimeTracker(), TimeTracker())) |
| 169 | 186 | ||
| 170 | # Gather per-task values | 187 | # Gather per-task values |
| 171 | read_data(task_dict, bins) | 188 | read_data(task_dict, bins) |
| 172 | 189 | ||
| 173 | stat_data = {"avg-tard" : [], "max-tard" : [], | 190 | saved_stats[data_dir] = task_dict |
| 174 | "avg-block" : [], "max-block" : [], | 191 | return task_dict |
| 175 | "miss-ratio" : []} | 192 | |
| 193 | class LeveledArray(object): | ||
| 194 | """Groups statistics by the level of the task to which they apply""" | ||
| 195 | def __init__(self): | ||
| 196 | self.name = name | ||
| 197 | self.vals = defaultdict(lambda: defaultdict(lambda:[])) | ||
| 198 | |||
| 199 | def add(self, name, level, value): | ||
| 200 | if type(value) != type([]): | ||
| 201 | value = [value] | ||
| 202 | self.vals[name][task.config.level] += value | ||
| 176 | 203 | ||
| 177 | # Group per-task values | 204 | def write_measurements(self, result): |
| 205 | for stat_name, stat_data in self.vals.iteritems(): | ||
| 206 | for level, values in stat_data.iteritems(): | ||
| 207 | if not values: | ||
| 208 | continue | ||
| 209 | |||
| 210 | name = "%s%s" % ("%s-" % level if level else "", stat_name) | ||
| 211 | result[name] = Measurement(name).from_array(arr) | ||
| 212 | |||
| 213 | def extract_sched_data(result, data_dir, work_dir): | ||
| 214 | task_dict = get_task_data(data_dir, work_dir) | ||
| 215 | |||
| 216 | stat_data = LeveledArray() | ||
| 178 | for tdata in task_dict.itervalues(): | 217 | for tdata in task_dict.itervalues(): |
| 179 | if not tdata.params: | 218 | if not tdata.params: |
| 180 | # Currently unknown where these invalid tasks come from... | 219 | # Currently unknown where these invalid tasks come from... |
| @@ -184,15 +223,59 @@ def extract_sched_data(result, data_dir, work_dir): | |||
| 184 | # Scale average down to account for jobs with 0 tardiness | 223 | # Scale average down to account for jobs with 0 tardiness |
| 185 | avg_tard = tdata.misses.avg * miss_ratio | 224 | avg_tard = tdata.misses.avg * miss_ratio |
| 186 | 225 | ||
| 187 | stat_data["miss-ratio"].append(miss_ratio) | 226 | level = tdata.params.level |
| 188 | stat_data["avg-tard" ].append(avg_tard / tdata.params.wcet) | 227 | stat_data.add("miss-ratio", level, miss_ratio) |
| 189 | stat_data["max-tard" ].append(tdata.misses.max / tdata.params.wcet) | 228 | stat_data.add("avg-tard", level, avg_tard / tdata.params.wcet) |
| 190 | stat_data["avg-block" ].append(tdata.blocks.avg / NSEC_PER_MSEC) | 229 | stat_data.add("max-tard", level, tdata.misses.max / tdata.params.wcet) |
| 191 | stat_data["max-block" ].append(tdata.blocks.max / NSEC_PER_MSEC) | 230 | stat_data.add("avg-block", level, tdata.blocks.avg / NSEC_PER_MSEC) |
| 192 | 231 | stat_data.add("max-block", level, tdata.blocks.max / NSEC_PER_MSEC) | |
| 193 | # Summarize value groups | 232 | |
| 194 | for name, data in stat_data.iteritems(): | 233 | stat_data.write_measurements(result) |
| 195 | if not data: | 234 | |
| 235 | ScaleData = namedtuple('ScaleData', ['reg_tasks', 'base_tasks']) | ||
| 236 | def extract_mc_data(result, data_dir, base_dir): | ||
| 237 | task_dict = get_task_data(data_dir) | ||
| 238 | base_dict = get_task_data(base_dir) | ||
| 239 | |||
| 240 | stat_data = LeveledArray() | ||
| 241 | |||
| 242 | # Only level B loads are measured | ||
| 243 | for tdata in filter(task_dict.iteritems(), lambda x: x.level == 'b'): | ||
| 244 | stat_data.add('load', tdata.config.level, tdata.loads) | ||
| 245 | |||
| 246 | tasks_by_config = defaultdict(lambda: ScaleData([], [])) | ||
| 247 | |||
| 248 | # Add tasks in order of pid to tasks_by_config | ||
| 249 | # Tasks must be ordered by pid or we can't make 1 to 1 comparisons | ||
| 250 | # when multiple tasks have the same config in each task set | ||
| 251 | for tasks, field in ((task_dict, 'reg_tasks'), (base_dict, 'base_tasks')): | ||
| 252 | for pid in sorted(tasks.keys()): | ||
| 253 | tdata = tasks[pid] | ||
| 254 | tlist = getattr(tasks_by_config[tdata.params], field) | ||
| 255 | tlist += [tdata.execs] | ||
| 256 | |||
| 257 | # Write scaling factors | ||
| 258 | for config, scale_data in tasks_by_config: | ||
| 259 | if len(scale_data.reg_tasks) != len(scale_data.base_tasks): | ||
| 260 | # Can't make comparison if different numbers of tasks! | ||
| 196 | continue | 261 | continue |
| 197 | result[name] = Measurement(str(name)).from_array(data) | ||
| 198 | 262 | ||
| 263 | all_pairs = zip(scale_data.reg_tasks, scale_data.base_tasks) | ||
| 264 | for reg_execs, base_execs in all_pairs: | ||
| 265 | if not reg_execs.max or not reg_execs.avg or\ | ||
| 266 | not base_execs.max or not base_execs.avg: | ||
| 267 | # This was an issue at some point, not sure if it still is | ||
| 268 | continue | ||
| 269 | |||
| 270 | max_scale = float(base_execs.max) / reg_execs.max | ||
| 271 | avg_scale = float(base_execs.avg) / reg_execs.avg | ||
| 272 | |||
| 273 | if (avg_scale < 1 or max_scale < 1) and config.level == "b": | ||
| 274 | sys.stderr.write("Task in {} with config {} has <1.0 scale!" | ||
| 275 | .format(data_dir, config) | ||
| 276 | continue | ||
| 277 | |||
| 278 | stat_data.add('max-scale', config.level, max_scale) | ||
| 279 | stat_data.add('avg-scale', config.level, avg_scale) | ||
| 280 | |||
| 281 | stat_data.write_measurements(result) | ||
