diff options
| author | Glenn Elliott <gelliott@cs.unc.edu> | 2014-01-14 15:02:33 -0500 |
|---|---|---|
| committer | Glenn Elliott <gelliott@cs.unc.edu> | 2014-01-14 15:02:33 -0500 |
| commit | f3106e83c7404e9de96117770f210cf6a207cc2d (patch) | |
| tree | ee2343ebfd8cdf41c3d85d4c654faa0d65f06cd6 | |
| parent | 1a000342dacd3dce7859c07dcf664852b21d7793 (diff) | |
Manual merge of Namhoon's PGM exp generation
This patch merges in the work Namhoon did to support
PGM-based task set experiments.
| -rw-r--r-- | config/config.py | 3 | ||||
| -rw-r--r-- | gen/__init__.py | 9 | ||||
| -rw-r--r-- | gen/edf_generators.py | 102 | ||||
| -rw-r--r-- | gen/generator.py | 176 | ||||
| -rwxr-xr-x | gen_pgm_exps.py | 122 | ||||
| -rw-r--r-- | run/experiment.py | 21 | ||||
| -rwxr-xr-x | run_exps.py | 11 |
7 files changed, 414 insertions, 30 deletions
diff --git a/config/config.py b/config/config.py index 976974a..f2d068c 100644 --- a/config/config.py +++ b/config/config.py | |||
| @@ -38,7 +38,8 @@ PARAMS = {'sched' : 'scheduler', # Scheduler used by run_exps | |||
| 38 | 38 | ||
| 39 | '''Default values for program options.''' | 39 | '''Default values for program options.''' |
| 40 | DEFAULTS = {'duration' : 10, | 40 | DEFAULTS = {'duration' : 10, |
| 41 | 'prog' : 'rtspin', | 41 | # 'prog' : 'rtspin', |
| 42 | 'prog' : 'pgmrt', | ||
| 42 | 'out-gen' : 'exps', | 43 | 'out-gen' : 'exps', |
| 43 | 'out-run' : 'run-data', | 44 | 'out-run' : 'run-data', |
| 44 | 'out-parse' : 'parse-data', | 45 | 'out-parse' : 'parse-data', |
diff --git a/gen/__init__.py b/gen/__init__.py index 654ac19..58f5cfa 100644 --- a/gen/__init__.py +++ b/gen/__init__.py | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | import generator as gen | 1 | import generator as gen |
| 2 | import edf_generators as edf | 2 | import edf_generators as edf |
| 3 | 3 | ||
| 4 | gen.register_generator("G-EDF", edf.GedfGenerator) | 4 | #gen.register_generator("G-EDF", edf.GedfGenerator) |
| 5 | gen.register_generator("P-EDF", edf.PedfGenerator) | 5 | #gen.register_generator("P-EDF", edf.PedfGenerator) |
| 6 | gen.register_generator("C-EDF", edf.CedfGenerator) | 6 | #gen.register_generator("C-EDF", edf.CedfGenerator) |
| 7 | gen.register_generator("C-FL-split", edf.CflSplitGenerator) | 7 | #gen.register_generator("C-FL-split", edf.CflSplitGenerator) |
| 8 | gen.register_generator("C-FL-split", edf.CflSplitPgmGenerator) | ||
diff --git a/gen/edf_generators.py b/gen/edf_generators.py index ce99d05..58cbb0d 100644 --- a/gen/edf_generators.py +++ b/gen/edf_generators.py | |||
| @@ -1,5 +1,13 @@ | |||
| 1 | import generator as gen | 1 | import generator as gen |
| 2 | import random | 2 | import random |
| 3 | import ecrts14.partition as partition | ||
| 4 | import schedcat.sched.split_heuristic as split | ||
| 5 | |||
| 6 | from ecrts14.ecrts14 import create_pgm_task_set | ||
| 7 | from ecrts14.ecrts14 import get_overheads | ||
| 8 | from config.config import FILES,PARAMS | ||
| 9 | from schedcat.overheads.model import Overheads, CacheDelay, ConsumerOverheads, ProducerOverheads | ||
| 10 | from ecrts14.tests import get_partitions | ||
| 3 | 11 | ||
| 4 | TP_TBASE = """#for $t in $task_set | 12 | TP_TBASE = """#for $t in $task_set |
| 5 | {} $t.cost $t.period | 13 | {} $t.cost $t.period |
| @@ -8,6 +16,15 @@ TP_GLOB_TASK = TP_TBASE.format("") | |||
| 8 | TP_PART_TASK = TP_TBASE.format("-p $t.cpu") | 16 | TP_PART_TASK = TP_TBASE.format("-p $t.cpu") |
| 9 | TP_CLST_TASK = TP_TBASE.format("-p $t.cluster -z $t.cluster_sz") | 17 | TP_CLST_TASK = TP_TBASE.format("-p $t.cluster -z $t.cluster_sz") |
| 10 | 18 | ||
| 19 | PARTITION_METHOD = { | ||
| 20 | 'no_cache' : partition.partition_no_cache, | ||
| 21 | 'parallel' : partition.partition_parallel, | ||
| 22 | 'cache_aware' : partition.partition_cache_aware, | ||
| 23 | 'cache_aware_edges' : partition.partition_cache_aware_edges, | ||
| 24 | 'cache_aware_bfs' : partition.partition_cache_aware_bfs, | ||
| 25 | 'cache_aware_dfs' : partition.partition_cache_aware_dfs, | ||
| 26 | } | ||
| 27 | |||
| 11 | class EdfGenerator(gen.Generator): | 28 | class EdfGenerator(gen.Generator): |
| 12 | '''Creates sporadic task sets with the most common Litmus options.''' | 29 | '''Creates sporadic task sets with the most common Litmus options.''' |
| 13 | def __init__(self, scheduler, templates, options, params): | 30 | def __init__(self, scheduler, templates, options, params): |
| @@ -39,12 +56,51 @@ class EdfGenerator(gen.Generator): | |||
| 39 | self._customize(ts, exp_params) | 56 | self._customize(ts, exp_params) |
| 40 | 57 | ||
| 41 | self._write_schedule(dict(exp_params.items() + [('task_set', ts)])) | 58 | self._write_schedule(dict(exp_params.items() + [('task_set', ts)])) |
| 59 | |||
| 42 | self._write_params(exp_params) | 60 | self._write_params(exp_params) |
| 43 | 61 | ||
| 44 | def _customize(self, taskset, exp_params): | 62 | def _customize(self, taskset, exp_params): |
| 45 | '''Configure a generated taskset with extra parameters.''' | 63 | '''Configure a generated taskset with extra parameters.''' |
| 46 | pass | 64 | pass |
| 47 | 65 | ||
| 66 | class EdfPgmGenerator(gen.Generator): | ||
| 67 | '''Creates sporadic task sets with the most common Litmus options.''' | ||
| 68 | def __init__(self, scheduler, templates, options, params): | ||
| 69 | super(EdfPgmGenerator, self).__init__(scheduler, templates, | ||
| 70 | self.__make_options() + options, | ||
| 71 | params) | ||
| 72 | |||
| 73 | def __make_options(self): | ||
| 74 | '''Return generic EDF options.''' | ||
| 75 | return [gen.Generator._dist_option('utils', 'uni-medium', | ||
| 76 | gen.NAMED_UTILIZATIONS, | ||
| 77 | 'Task utilization distributions.'), | ||
| 78 | gen.Generator._dist_option('periods', 'harmonic', | ||
| 79 | gen.NAMED_PERIODS, | ||
| 80 | 'Task period distributions.')] | ||
| 81 | |||
| 82 | def _create_exp(self, dp, ts, graphs, subts): | ||
| 83 | '''Create a single experiment with @exp_params in @out_dir.''' | ||
| 84 | |||
| 85 | ts = self._customize(ts, graphs, subts, dp) | ||
| 86 | |||
| 87 | self._write_pgm_schedule(dict(dp.items() + [('task_set', ts)] + [('graphs', graphs)] + [('sub_task_set', subts)])) | ||
| 88 | self._write_params(dict(dp.items() + [('num_tasks', len(ts))])) | ||
| 89 | |||
| 90 | def _create_tasks(self, dp): | ||
| 91 | '''Create a task set.''' | ||
| 92 | ts, graphs, subts = create_pgm_task_set(dp) | ||
| 93 | # convert to ms | ||
| 94 | for t in ts: | ||
| 95 | t.cost = t.cost / 1000.0 | ||
| 96 | t.period = t.period / 1000.0 | ||
| 97 | t.deadline = t.deadline /1000.0 | ||
| 98 | |||
| 99 | return ts, graphs, subts | ||
| 100 | |||
| 101 | def _customize(self, taskset, exp_params): | ||
| 102 | '''Configure a generated taskset with extra parameters.''' | ||
| 103 | pass | ||
| 48 | 104 | ||
| 49 | class PartitionedGenerator(EdfGenerator): | 105 | class PartitionedGenerator(EdfGenerator): |
| 50 | def __init__(self, scheduler, templates, options, params): | 106 | def __init__(self, scheduler, templates, options, params): |
| @@ -88,9 +144,6 @@ class CflSplitGenerator(EdfGenerator): | |||
| 88 | TP_CLUSTER = "plugins/C-FL-split/cluster{$level}" | 144 | TP_CLUSTER = "plugins/C-FL-split/cluster{$level}" |
| 89 | CLUSTER_OPTION = gen.GenOption('level', ['L1', 'L2', 'L3', 'ALL'], 'L2', | 145 | CLUSTER_OPTION = gen.GenOption('level', ['L1', 'L2', 'L3', 'ALL'], 'L2', |
| 90 | 'Cache clustering level.',) | 146 | 'Cache clustering level.',) |
| 91 | # CLUSTER_SZ_OPTION = gen.GenOption('cluster_size', ['1', '2', '6', '24'], '2', | ||
| 92 | # 'Cluster size.',) | ||
| 93 | |||
| 94 | def __init__(self, params={}): | 147 | def __init__(self, params={}): |
| 95 | super(CflSplitGenerator, self).__init__("C-FL-split", | 148 | super(CflSplitGenerator, self).__init__("C-FL-split", |
| 96 | [CflSplitGenerator.TP_CLUSTER, TP_CLST_TASK], | 149 | [CflSplitGenerator.TP_CLUSTER, TP_CLST_TASK], |
| @@ -99,8 +152,6 @@ class CflSplitGenerator(EdfGenerator): | |||
| 99 | 152 | ||
| 100 | def _customize(self, taskset, exp_params): | 153 | def _customize(self, taskset, exp_params): |
| 101 | cpus = int(exp_params['cpus']) | 154 | cpus = int(exp_params['cpus']) |
| 102 | # cluster_sz = int(exp_params['cluster_size']) | ||
| 103 | |||
| 104 | if exp_params['level'] == 'L1': | 155 | if exp_params['level'] == 'L1': |
| 105 | cluster_sz = 1 | 156 | cluster_sz = 1 |
| 106 | elif exp_params['level'] == 'L2': | 157 | elif exp_params['level'] == 'L2': |
| @@ -127,3 +178,44 @@ class GedfGenerator(EdfGenerator): | |||
| 127 | def __init__(self, params={}): | 178 | def __init__(self, params={}): |
| 128 | super(GedfGenerator, self).__init__("GSN-EDF", [TP_GLOB_TASK], | 179 | super(GedfGenerator, self).__init__("GSN-EDF", [TP_GLOB_TASK], |
| 129 | [], params) | 180 | [], params) |
| 181 | |||
| 182 | class CflSplitPgmGenerator(EdfPgmGenerator): | ||
| 183 | TP_CLUSTER = "plugins/C-FL-split/cluster{$level}" | ||
| 184 | CLUSTER_OPTION = gen.GenOption('level', ['L1', 'L2', 'L3', 'ALL'], 'L2', | ||
| 185 | 'Cache clustering level.',) | ||
| 186 | |||
| 187 | def __init__(self, params={}): | ||
| 188 | super(CflSplitPgmGenerator, self).__init__("C-FL-split", | ||
| 189 | [CflSplitPgmGenerator.TP_CLUSTER], | ||
| 190 | [CflSplitPgmGenerator.CLUSTER_OPTION], | ||
| 191 | params) | ||
| 192 | |||
| 193 | def _customize(self, ts, graphs, subts, dp): | ||
| 194 | exp_params = dict(dp.items()) | ||
| 195 | cpus = int(exp_params['cpus']) | ||
| 196 | |||
| 197 | if exp_params['level'] == 'L1': | ||
| 198 | cluster_sz = 1 | ||
| 199 | elif exp_params['level'] == 'L2': | ||
| 200 | cluster_sz = 2 | ||
| 201 | elif exp_params['level'] == 'L3': | ||
| 202 | cluster_sz = 6 | ||
| 203 | elif exp_params['level'] == 'ALL': | ||
| 204 | cluster_sz = 24 | ||
| 205 | else: | ||
| 206 | assert False | ||
| 207 | |||
| 208 | dp.nr_clusters = cpus / cluster_sz | ||
| 209 | assert dp.nr_clusters * cluster_sz == cpus | ||
| 210 | |||
| 211 | overheads = get_overheads(dp, dp.system) | ||
| 212 | # do the partition here | ||
| 213 | ts = partition.clear_partitioning(ts) | ||
| 214 | ts = PARTITION_METHOD[exp_params['partitions']](ts, graphs, subts, cluster_sz, dp.nr_clusters, dp.system, dp.heur_aggressiveness, overheads) | ||
| 215 | |||
| 216 | # compute split factor | ||
| 217 | working_ts = ts | ||
| 218 | partitions = get_partitions(working_ts, dp.nr_clusters, cluster_sz) | ||
| 219 | is_srt_sched = split.compute_splits_nolock(overheads, False, working_ts, partitions, bypass_split = not dp.job_splitting) | ||
| 220 | |||
| 221 | return working_ts | ||
diff --git a/gen/generator.py b/gen/generator.py index 0999e84..7456021 100644 --- a/gen/generator.py +++ b/gen/generator.py | |||
| @@ -3,12 +3,18 @@ import os | |||
| 3 | import pprint | 3 | import pprint |
| 4 | import schedcat.generator.tasks as tasks | 4 | import schedcat.generator.tasks as tasks |
| 5 | import shutil as sh | 5 | import shutil as sh |
| 6 | import ecrts14.topology as topology | ||
| 7 | import ecrts14.graph as graph | ||
| 6 | 8 | ||
| 7 | from Cheetah.Template import Template | 9 | from Cheetah.Template import Template |
| 8 | from common import get_config_option,num_cpus,recordtype | 10 | from common import get_config_option,num_cpus,recordtype |
| 9 | from config.config import FILES,PARAMS | 11 | from config.config import FILES,PARAMS |
| 10 | from gen.dp import DesignPointGenerator | 12 | from gen.dp import DesignPointGenerator |
| 13 | from ecrts14.generator import DesignPointGenerator as PgmDesignPointGenerator | ||
| 11 | from parse.col_map import ColMapBuilder | 14 | from parse.col_map import ColMapBuilder |
| 15 | from numpy import arange | ||
| 16 | from schedcat.util.storage import storage | ||
| 17 | from ecrts14.machines import machines | ||
| 12 | 18 | ||
| 13 | NAMED_PERIODS = { | 19 | NAMED_PERIODS = { |
| 14 | 'harmonic' : rv.uniform_choice([25, 50, 100, 200]), | 20 | 'harmonic' : rv.uniform_choice([25, 50, 100, 200]), |
| @@ -75,13 +81,14 @@ class Generator(object): | |||
| 75 | release_master = list(set([False, bool(rm_config)])) | 81 | release_master = list(set([False, bool(rm_config)])) |
| 76 | 82 | ||
| 77 | 83 | ||
| 78 | return [GenOption('tasks', int, range(cpus, 5*cpus, cpus), | 84 | return [GenOption('partitions', str, ['no_cache', 'parallel', 'cache_aware', 'cache_aware_edges', 'cache_aware_bfs', 'cache_aware_dfs'], |
| 79 | 'Number of tasks per experiment.'), | 85 | 'Partition methods.'), |
| 86 | GenOption('tasks', int, [0], | ||
| 87 | 'Number of tasks'), | ||
| 80 | GenOption('cpus', int, [cpus], | 88 | GenOption('cpus', int, [cpus], |
| 81 | 'Number of processors on target system.'), | 89 | 'Number of processors on target system.'), |
| 82 | GenOption('release_master', [True,False], release_master, | 90 | GenOption('release_master', [True,False], release_master, |
| 83 | 'Redirect release interrupts to a single CPU.'), | 91 | 'Redirect release interrupts to a single CPU.')] |
| 84 | GenOption('duration', float, [30], 'Experiment duration.')] | ||
| 85 | 92 | ||
| 86 | @staticmethod | 93 | @staticmethod |
| 87 | def _dist_option(name, default, distribution, help): | 94 | def _dist_option(name, default, distribution, help): |
| @@ -122,14 +129,93 @@ class Generator(object): | |||
| 122 | def _write_schedule(self, params): | 129 | def _write_schedule(self, params): |
| 123 | '''Write schedule file using current template for @params.''' | 130 | '''Write schedule file using current template for @params.''' |
| 124 | sched_file = self.out_dir + "/" + FILES['sched_file'] | 131 | sched_file = self.out_dir + "/" + FILES['sched_file'] |
| 132 | |||
| 125 | with open(sched_file, 'wa') as f: | 133 | with open(sched_file, 'wa') as f: |
| 126 | f.write(str(Template(self.template, searchList=[params]))) | 134 | f.write(str(Template(self.template, searchList=[params]))) |
| 127 | 135 | ||
| 136 | def _write_pgm_schedule(self, pgm_params): | ||
| 137 | '''Write schedule file using current template for @params.''' | ||
| 138 | # make pgmrt arguments using graphs and tasks. | ||
| 139 | sched_file = self.out_dir + "/" + FILES['sched_file'] | ||
| 140 | |||
| 141 | graph_desc_arg = [] | ||
| 142 | rates_arg = [] | ||
| 143 | exec_arg = [] | ||
| 144 | cluster_arg = [] | ||
| 145 | clustersz_arg = [] | ||
| 146 | wss_arg = [] | ||
| 147 | split_arg = [] | ||
| 148 | for g in pgm_params['graphs']: | ||
| 149 | cluster_arg_t = [] | ||
| 150 | graph_desc_arg_t = [] | ||
| 151 | exec_arg_t = [] | ||
| 152 | rates_arg_t = [] | ||
| 153 | wss_arg_t = [] | ||
| 154 | split_arg_t = [] | ||
| 155 | |||
| 156 | for n in g.nodes: | ||
| 157 | assert n.graph.id == g.id | ||
| 158 | cluster_arg_t.append('node_' + str(n.id) + ':' + str(n.task.partition)) | ||
| 159 | exec_arg_t.append('node_' + str(n.id) + ':' + str(n.task.cost)) | ||
| 160 | split_arg_t.append('node_' + str(n.id) + ':' + str(n.task.split)) | ||
| 161 | if n.isSrc == True: | ||
| 162 | # assume that x=1 | ||
| 163 | rates_arg_t.append('node_' + str(n.id) + ':1:' + str(n.task.period)) | ||
| 164 | # get cluster size | ||
| 165 | clustersz_arg_t = str(pgm_params['cpus'] / pgm_params['nr_clusters']) | ||
| 166 | if len(g.nodes) == 1: | ||
| 167 | graph_desc_arg_t.append('node_' + str(n.id)) | ||
| 168 | for succ in n.succ: | ||
| 169 | graph_desc_arg_t.append('node_' + str(n.id) + ':node_' + str(succ.id)) | ||
| 170 | # wss parameter | ||
| 171 | for e in n.outEdges: | ||
| 172 | wss_arg_t.append('node_' + str(n.id) + ':node_' + str(e.s.id) + ':' + str(e.wss)) | ||
| 173 | |||
| 174 | # combine arguments to a comma-separated string | ||
| 175 | cluster_arg_t = ','.join(cluster_arg_t) | ||
| 176 | graph_desc_arg_t = ','.join(graph_desc_arg_t) | ||
| 177 | exec_arg_t = ','.join(exec_arg_t) | ||
| 178 | wss_arg_t = ','.join(wss_arg_t) | ||
| 179 | split_arg_t = ','.join(split_arg_t) | ||
| 180 | rates_arg_t = ','.join(rates_arg_t) | ||
| 181 | |||
| 182 | cluster_arg.append(cluster_arg_t) | ||
| 183 | exec_arg.append(exec_arg_t) | ||
| 184 | graph_desc_arg.append(graph_desc_arg_t) | ||
| 185 | wss_arg.append(wss_arg_t) | ||
| 186 | split_arg.append(split_arg_t) | ||
| 187 | rates_arg.append(rates_arg_t) | ||
| 188 | clustersz_arg.append(clustersz_arg_t) | ||
| 189 | #print('----------') | ||
| 190 | #print(pgm_params) | ||
| 191 | |||
| 192 | pgm_args = [] | ||
| 193 | for i in range(len(pgm_params['graphs'])): | ||
| 194 | pgm_args_t = ''; | ||
| 195 | pgm_args_t += '--wait --cluster ' + cluster_arg[i] + ' --clusterSize ' + clustersz_arg[i] + ' --name graph_' + str(pgm_params['graphs'][i].id) | ||
| 196 | pgm_args_t += ' --graph ' + graph_desc_arg[i] + ' --rates ' + rates_arg[i] + ' --execution ' + exec_arg[i] | ||
| 197 | pgm_args_t += ' --split ' + split_arg[i] | ||
| 198 | if len(wss_arg[i]) != 0: | ||
| 199 | pgm_args_t += ' --wss ' + wss_arg[i] | ||
| 200 | |||
| 201 | # pgm_args_t += ' --duration ' + str(pgm_params['duration']) | ||
| 202 | # last argument must always be duration. actual duration given by run_exps.py | ||
| 203 | pgm_args_t += ' --duration' | ||
| 204 | |||
| 205 | pgm_args.append(pgm_args_t) | ||
| 206 | |||
| 207 | with open(sched_file, 'wa') as f: | ||
| 208 | f.write(str(Template(self.template, searchList=[pgm_params])) + '\n') | ||
| 209 | for s in pgm_args: | ||
| 210 | f.write(s + '\n') | ||
| 128 | 211 | ||
| 129 | def _write_params(self, params): | 212 | def _write_params(self, params): |
| 130 | '''Write out file with relevant parameters.''' | 213 | '''Write out file with relevant parameters.''' |
| 131 | # Don't include this in the parameters. It will be automatically added | 214 | # Don't include this in the parameters. It will be automatically added |
| 132 | # in run_exps.py | 215 | # in run_exps.py |
| 216 | if 'system' in params: | ||
| 217 | del params['system'] | ||
| 218 | |||
| 133 | if 'tasks' in params: | 219 | if 'tasks' in params: |
| 134 | tasks = params.pop('tasks') | 220 | tasks = params.pop('tasks') |
| 135 | else: | 221 | else: |
| @@ -240,6 +326,88 @@ class Generator(object): | |||
| 240 | 326 | ||
| 241 | HELP_INDENT = 17 | 327 | HELP_INDENT = 17 |
| 242 | 328 | ||
| 329 | def create_pgm_exps(self, opts): | ||
| 330 | '''Create experiments for all possible combinations of params in | ||
| 331 | @out_dir. Overwrite existing files if @force is True.''' | ||
| 332 | builder = ColMapBuilder() | ||
| 333 | |||
| 334 | out_dir = opts.out_dir | ||
| 335 | force = opts.force | ||
| 336 | trials = opts.trials | ||
| 337 | |||
| 338 | # hard coded here | ||
| 339 | exp = storage() | ||
| 340 | exp.host = ['ludwig'] | ||
| 341 | cpus = 24.0 | ||
| 342 | exp.processors = [cpus] | ||
| 343 | exp.task_util = ['uni-medium'] | ||
| 344 | exp.period = ['uni-long'] | ||
| 345 | exp.sched = ['edf'] | ||
| 346 | exp.sys_util = [ 20.0 ] #arange(1, cpus+0.1, 0.1) | ||
| 347 | |||
| 348 | exp.wcycle = [ 0 ] | ||
| 349 | exp.walk = ['seq'] | ||
| 350 | exp.huge_pages = [False] | ||
| 351 | exp.uncached = [False] | ||
| 352 | exp.polluters = [False] | ||
| 353 | exp.ovh_type = ['max'] | ||
| 354 | exp.release_master = [False] | ||
| 355 | exp.heur_aggressiveness = [0.75] | ||
| 356 | exp.job_splitting = [True] | ||
| 357 | exp.update(self.params) | ||
| 358 | |||
| 359 | # Track changing values so only relevant parameters are included | ||
| 360 | # in directory names | ||
| 361 | for dp in PgmDesignPointGenerator(exp): | ||
| 362 | for k, v in dp.iteritems(): | ||
| 363 | builder.try_add(k, v) | ||
| 364 | col_map = builder.build() | ||
| 365 | |||
| 366 | for trial in xrange(trials): | ||
| 367 | dp.num_graphs = graph.uniform(opts.num_graphs, opts.num_graphs) | ||
| 368 | dp.depth_factor = [1.0/3.0, 2.0/3.0] | ||
| 369 | dp.node_placement = graph.uniform(opts.node_placement, opts.node_placement) | ||
| 370 | dp.fan_out = graph.geometric(1, opts.fan_out) | ||
| 371 | dp.fan_in_cap = opts.fan_in_cap | ||
| 372 | dp.edge_distance = graph.geometric(1, opts.edge_distance) | ||
| 373 | dp.nr_source = graph.uniform(opts.nr_source, opts.nr_source) | ||
| 374 | dp.nr_sink = graph.uniform(opts.nr_sink, opts.nr_sink) | ||
| 375 | dp.wss = tasks.multimodal([(tasks.uniform_int(1,2), 6), (tasks.uniform_int(2, 8), 3)]) | ||
| 376 | |||
| 377 | # Generate a task set | ||
| 378 | ts, graphs, subts = self._create_tasks(dp) | ||
| 379 | dp.tasks = len(ts) | ||
| 380 | |||
| 381 | for dp in PgmDesignPointGenerator(exp): | ||
| 382 | # Create directory name from relevant parameters | ||
| 383 | dir_leaf = "sched=%s_%s" % (self.scheduler, col_map.encode(dp)) | ||
| 384 | dir_leaf = dir_leaf.strip('_') # If there are none | ||
| 385 | dir_leaf += ("_trial=%s" % trial) if trials > 1 else "" | ||
| 386 | |||
| 387 | dir_path = "%s/%s" % (out_dir, dir_leaf.strip('_')) | ||
| 388 | |||
| 389 | if os.path.exists(dir_path): | ||
| 390 | if force: | ||
| 391 | sh.rmtree(dir_path) | ||
| 392 | else: | ||
| 393 | print("Skipping existing experiment: '%s'" % dir_path) | ||
| 394 | continue | ||
| 395 | |||
| 396 | os.mkdir(dir_path) | ||
| 397 | |||
| 398 | if trials > 1: | ||
| 399 | dp[PARAMS['trial']] = trial | ||
| 400 | self.out_dir = dir_path | ||
| 401 | |||
| 402 | dp.system = topology.Topology(machines[dp.host]) | ||
| 403 | |||
| 404 | # Write a sched.py and param.py for each partition method | ||
| 405 | self._create_exp(dp, ts, graphs, subts) | ||
| 406 | |||
| 407 | del(self.out_dir) | ||
| 408 | if PARAMS['trial'] in dp: | ||
| 409 | del dp[PARAMS['trial']] | ||
| 410 | |||
| 243 | def print_help(self): | 411 | def print_help(self): |
| 244 | display_options = [o for o in self.options if not o.hidden] | 412 | display_options = [o for o in self.options if not o.hidden] |
| 245 | s = str(Template("""scheduler $scheduler: | 413 | s = str(Template("""scheduler $scheduler: |
diff --git a/gen_pgm_exps.py b/gen_pgm_exps.py new file mode 100755 index 0000000..62723ec --- /dev/null +++ b/gen_pgm_exps.py | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | from __future__ import print_function | ||
| 3 | |||
| 4 | import gen.generator as gen | ||
| 5 | import os | ||
| 6 | import re | ||
| 7 | import shutil as sh | ||
| 8 | import sys | ||
| 9 | |||
| 10 | from config.config import DEFAULTS | ||
| 11 | from optparse import OptionParser | ||
| 12 | |||
| 13 | import ecrts14.graph as graph | ||
| 14 | import schedcat.generator.tasks as tasks | ||
| 15 | |||
| 16 | def parse_args(): | ||
| 17 | parser = OptionParser("usage: %prog [options] [files...] " | ||
| 18 | "[generators...] [param=val[,val]...]") | ||
| 19 | |||
| 20 | parser.add_option('-o', '--out-dir', dest='out_dir', | ||
| 21 | help='directory for data output', | ||
| 22 | default=("%s/%s"% (os.getcwd(), DEFAULTS['out-gen']))) | ||
| 23 | parser.add_option('-f', '--force', action='store_true', default=True, | ||
| 24 | dest='force', help='overwrite existing data') | ||
| 25 | parser.add_option('-n', '--num-trials', default=1, type='int', dest='trials', | ||
| 26 | help='number of task systems for every config') | ||
| 27 | parser.add_option('-l', '--list-generators', dest='list_gens', | ||
| 28 | help='list allowed generators', action='store_true', | ||
| 29 | default=False) | ||
| 30 | parser.add_option('-d', '--describe-generators', metavar='generator[,..]', | ||
| 31 | dest='described', default=None, | ||
| 32 | help='describe parameters for generator(s)') | ||
| 33 | parser.add_option('-m', '--num-graphs', default=24, type='int', dest='num_graphs', | ||
| 34 | help='number of graphs for a taskset') | ||
| 35 | parser.add_option('-p', '--node-placement', default=1, type='int', dest='node_placement', | ||
| 36 | help='node placement of the graph') | ||
| 37 | parser.add_option('-t', '--fan-out', default=3, type='int', dest='fan_out', | ||
| 38 | help='fan out of a node') | ||
| 39 | parser.add_option('-i', '--fan-in-cap', default=3, type='int', dest='fan_in_cap', | ||
| 40 | help='fan in cap') | ||
| 41 | parser.add_option('-g', '--edge-distance', default=3, type='int', dest='edge_distance', | ||
| 42 | help='edge distance') | ||
| 43 | parser.add_option('-u', '--nr-source', default=1, type='int', dest='nr_source', | ||
| 44 | help='number of source nodes') | ||
| 45 | parser.add_option('-v', '--nr_sink', default=1, type='int', dest='nr_sink', | ||
| 46 | help='number of sink nodes') | ||
| 47 | |||
| 48 | |||
| 49 | return parser.parse_args() | ||
| 50 | |||
| 51 | def load_file(fname): | ||
| 52 | with open(fname, 'r') as f: | ||
| 53 | data = f.read().strip() | ||
| 54 | try: | ||
| 55 | values = eval(data) | ||
| 56 | if 'generator' not in values: | ||
| 57 | raise ValueError() | ||
| 58 | generator = values['generator'] | ||
| 59 | del values['generator'] | ||
| 60 | return generator, values | ||
| 61 | except: | ||
| 62 | raise IOError("Invalid generation file: %s" % fname) | ||
| 63 | |||
| 64 | def print_descriptions(described): | ||
| 65 | for generator in described.split(','): | ||
| 66 | if generator not in gen.get_generators(): | ||
| 67 | sys.stderr.write("No generator '%s'\n" % generator) | ||
| 68 | else: | ||
| 69 | print("Generator '%s', " % generator) | ||
| 70 | gen.get_generators()[generator]().print_help() | ||
| 71 | |||
| 72 | def main(): | ||
| 73 | opts, args = parse_args() | ||
| 74 | |||
| 75 | # Print generator information on the command line | ||
| 76 | if opts.list_gens: | ||
| 77 | print(", ".join(gen.get_generators())) | ||
| 78 | if opts.described != None: | ||
| 79 | print_descriptions(opts.described) | ||
| 80 | if opts.list_gens or opts.described: | ||
| 81 | return 0 | ||
| 82 | |||
| 83 | params = filter(lambda x : re.match("\w+=\w+", x), args) | ||
| 84 | |||
| 85 | # Ensure some generator is loaded | ||
| 86 | args = list(set(args) - set(params)) | ||
| 87 | args = args or gen.get_generators().keys() | ||
| 88 | |||
| 89 | # Split into files to load and named generators | ||
| 90 | files = filter(os.path.exists, args) | ||
| 91 | gen_list = list(set(args) - set(files)) | ||
| 92 | |||
| 93 | # Parse all specified parameters to be applied to every experiment | ||
| 94 | global_params = dict(map(lambda x : tuple(x.split("=")), params)) | ||
| 95 | for k, v in global_params.iteritems(): | ||
| 96 | global_params[k] = v.split(',') | ||
| 97 | |||
| 98 | exp_sets = map(load_file, files) | ||
| 99 | exp_sets += map(lambda x: (x, {}), gen_list) | ||
| 100 | |||
| 101 | if opts.force and os.path.exists(opts.out_dir): | ||
| 102 | sh.rmtree(opts.out_dir) | ||
| 103 | if not os.path.exists(opts.out_dir): | ||
| 104 | os.mkdir(opts.out_dir) | ||
| 105 | |||
| 106 | for gen_name, gen_params in exp_sets: | ||
| 107 | if gen_name not in gen.get_generators(): | ||
| 108 | raise ValueError("Invalid generator '%s'" % gen_name) | ||
| 109 | |||
| 110 | sys.stderr.write("Creating experiments with %s generator...\n" % gen_name) | ||
| 111 | |||
| 112 | params = dict(gen_params.items() + global_params.items()) | ||
| 113 | clazz = gen.get_generators()[gen_name] | ||
| 114 | |||
| 115 | generator = clazz(params=params) | ||
| 116 | |||
| 117 | generator.create_pgm_exps(opts) | ||
| 118 | |||
| 119 | sys.stderr.write("Experiments saved in %s.\n" % opts.out_dir) | ||
| 120 | |||
| 121 | if __name__ == '__main__': | ||
| 122 | main() | ||
diff --git a/run/experiment.py b/run/experiment.py index da0e32e..522e490 100644 --- a/run/experiment.py +++ b/run/experiment.py | |||
| @@ -23,7 +23,7 @@ class Experiment(object): | |||
| 23 | INTERRUPTED_DIR = ".interrupted" | 23 | INTERRUPTED_DIR = ".interrupted" |
| 24 | 24 | ||
| 25 | def __init__(self, name, scheduler, working_dir, finished_dir, | 25 | def __init__(self, name, scheduler, working_dir, finished_dir, |
| 26 | proc_entries, executables, tracer_types): | 26 | proc_entries, executables, tracer_types, num_tasks): |
| 27 | '''Run an experiment, optionally wrapped in tracing.''' | 27 | '''Run an experiment, optionally wrapped in tracing.''' |
| 28 | self.name = name | 28 | self.name = name |
| 29 | self.scheduler = scheduler | 29 | self.scheduler = scheduler |
| @@ -34,6 +34,7 @@ class Experiment(object): | |||
| 34 | self.exec_out = None | 34 | self.exec_out = None |
| 35 | self.exec_err = None | 35 | self.exec_err = None |
| 36 | self.tracer_types = tracer_types | 36 | self.tracer_types = tracer_types |
| 37 | self.num_tasks = num_tasks | ||
| 37 | 38 | ||
| 38 | self.regular_tracers = [] | 39 | self.regular_tracers = [] |
| 39 | self.exact_tracers = [] | 40 | self.exact_tracers = [] |
| @@ -134,11 +135,11 @@ class Experiment(object): | |||
| 134 | wait_start = time.time() | 135 | wait_start = time.time() |
| 135 | num_ready = lu.waiting_tasks() | 136 | num_ready = lu.waiting_tasks() |
| 136 | 137 | ||
| 137 | while num_ready < len(self.executables): | 138 | while num_ready < self.num_tasks: |
| 138 | # Quit if too much time passes without a task becoming ready | 139 | # Quit if too much time passes without a task becoming ready |
| 139 | if time.time() - wait_start > 180.0: | 140 | if time.time() - wait_start > 180.0: |
| 140 | s = "waiting: %d, submitted: %d" %\ | 141 | s = "waiting: %d, submitted: %d" %\ |
| 141 | (lu.waiting_tasks(), len(self.executables)) | 142 | (lu.waiting_tasks(), self.num_tasks) |
| 142 | raise Exception("Too much time spent waiting for tasks! %s" % s) | 143 | raise Exception("Too much time spent waiting for tasks! %s" % s) |
| 143 | 144 | ||
| 144 | time.sleep(1) | 145 | time.sleep(1) |
| @@ -153,14 +154,13 @@ class Experiment(object): | |||
| 153 | num_ready = now_ready | 154 | num_ready = now_ready |
| 154 | 155 | ||
| 155 | def __run_tasks(self): | 156 | def __run_tasks(self): |
| 156 | self.log("Starting %d tasks" % len(self.executables)) | 157 | self.log("Starting %d real-time tasks, %d 'pgmrt' instances" % (self.num_tasks, len(self.executables))) |
| 157 | 158 | ||
| 158 | for i,e in enumerate(self.executables): | 159 | for i,e in enumerate(self.executables): |
| 159 | try: | 160 | try: |
| 160 | e.execute() | 161 | e.execute() |
| 161 | except: | 162 | except: |
| 162 | raise Exception("Executable failed to start: %s" % e) | 163 | raise Exception("Executable failed to start: %s" % e) |
| 163 | |||
| 164 | self.__wait_for_ready() | 164 | self.__wait_for_ready() |
| 165 | 165 | ||
| 166 | # Exact tracers (like overheads) must be started right after release or | 166 | # Exact tracers (like overheads) must be started right after release or |
| @@ -170,14 +170,14 @@ class Experiment(object): | |||
| 170 | time.sleep(1) | 170 | time.sleep(1) |
| 171 | 171 | ||
| 172 | try: | 172 | try: |
| 173 | self.log("Releasing %d tasks" % len(self.executables)) | 173 | self.log("Releasing %d tasks" % self.num_tasks) |
| 174 | released = lu.release_tasks() | 174 | released = lu.release_tasks() |
| 175 | 175 | ||
| 176 | if released != len(self.executables): | 176 | if released != self.num_tasks: |
| 177 | # Some tasks failed to release, kill all tasks and fail | 177 | # Some tasks failed to release, kill all tasks and fail |
| 178 | # Need to release non-released tasks before they can be killed | 178 | # Need to release non-released tasks before they can be killed |
| 179 | raise Exception("Released %s tasks, expected %s tasks" % | 179 | raise Exception("Released %s tasks, expected %s tasks" % |
| 180 | (released, len(self.executables))) | 180 | (released, self.num_tasks)) |
| 181 | 181 | ||
| 182 | self.log("Waiting for program to finish...") | 182 | self.log("Waiting for program to finish...") |
| 183 | for e in self.executables: | 183 | for e in self.executables: |
| @@ -212,6 +212,7 @@ class Experiment(object): | |||
| 212 | 212 | ||
| 213 | def __setup(self): | 213 | def __setup(self): |
| 214 | self.__make_dirs() | 214 | self.__make_dirs() |
| 215 | self.__clean_up_shm() | ||
| 215 | self.__assign_executable_cwds() | 216 | self.__assign_executable_cwds() |
| 216 | self.__setup_tracers() | 217 | self.__setup_tracers() |
| 217 | 218 | ||
| @@ -244,6 +245,10 @@ class Experiment(object): | |||
| 244 | 245 | ||
| 245 | os.system('sync') | 246 | os.system('sync') |
| 246 | 247 | ||
| 248 | def __clean_up_shm(self): | ||
| 249 | self.log("Removing temporary files in /dev/shm") | ||
| 250 | os.system('rm /dev/shm/* -r') | ||
| 251 | |||
| 247 | def log(self, msg): | 252 | def log(self, msg): |
| 248 | print("[Exp %s]: %s" % (self.name, msg)) | 253 | print("[Exp %s]: %s" % (self.name, msg)) |
| 249 | 254 | ||
diff --git a/run_exps.py b/run_exps.py index 21666a9..3fff667 100755 --- a/run_exps.py +++ b/run_exps.py | |||
| @@ -65,7 +65,7 @@ def parse_args(): | |||
| 65 | 65 | ||
| 66 | parser.add_option('-s', '--scheduler', dest='scheduler', | 66 | parser.add_option('-s', '--scheduler', dest='scheduler', |
| 67 | help='scheduler for all experiments') | 67 | help='scheduler for all experiments') |
| 68 | parser.add_option('-d', '--duration', dest='duration', type='int', | 68 | parser.add_option('-d', '--duration', dest='duration', type='int', default=30, |
| 69 | help='duration (seconds) of tasks') | 69 | help='duration (seconds) of tasks') |
| 70 | parser.add_option('-i', '--ignore-environment', dest='ignore', | 70 | parser.add_option('-i', '--ignore-environment', dest='ignore', |
| 71 | action='store_true', default=False, | 71 | action='store_true', default=False, |
| @@ -109,7 +109,7 @@ def convert_data(data): | |||
| 109 | r"\s*{\s*(?P<CONTENT>.*?)\s*?}$)|" | 109 | r"\s*{\s*(?P<CONTENT>.*?)\s*?}$)|" |
| 110 | r"(?P<TASK>^" | 110 | r"(?P<TASK>^" |
| 111 | r"(?:(?P<PROG>[^\d\-\s][\w\.]*?) )?\s*" | 111 | r"(?:(?P<PROG>[^\d\-\s][\w\.]*?) )?\s*" |
| 112 | r"(?P<ARGS>[\w\-_\d\. \=]+)\s*$)", | 112 | r"(?P<ARGS>[\w\-_\d\. \=\:\,]+)\s*$)", |
| 113 | re.S|re.I|re.M) | 113 | re.S|re.I|re.M) |
| 114 | 114 | ||
| 115 | procs = [] | 115 | procs = [] |
| @@ -140,10 +140,8 @@ def fix_paths(schedule, exp_dir, sched_file): | |||
| 140 | elif re.match(r'.*\w+\.[a-zA-Z]\w*', arg): | 140 | elif re.match(r'.*\w+\.[a-zA-Z]\w*', arg): |
| 141 | sys.stderr.write("WARNING: non-existent file '%s' " % arg + | 141 | sys.stderr.write("WARNING: non-existent file '%s' " % arg + |
| 142 | "may be referenced:\n\t%s" % sched_file) | 142 | "may be referenced:\n\t%s" % sched_file) |
| 143 | |||
| 144 | schedule['task'][idx] = (task, args) | 143 | schedule['task'][idx] = (task, args) |
| 145 | 144 | ||
| 146 | |||
| 147 | def load_schedule(name, fname, duration): | 145 | def load_schedule(name, fname, duration): |
| 148 | '''Turn schedule file @fname into ProcEntry's and Executable's which execute | 146 | '''Turn schedule file @fname into ProcEntry's and Executable's which execute |
| 149 | for @duration time.''' | 147 | for @duration time.''' |
| @@ -153,7 +151,6 @@ def load_schedule(name, fname, duration): | |||
| 153 | schedule = eval(data) | 151 | schedule = eval(data) |
| 154 | except: | 152 | except: |
| 155 | schedule = convert_data(data) | 153 | schedule = convert_data(data) |
| 156 | |||
| 157 | sched_dir = os.path.split(fname)[0] | 154 | sched_dir = os.path.split(fname)[0] |
| 158 | 155 | ||
| 159 | # Make paths relative to the file's directory | 156 | # Make paths relative to the file's directory |
| @@ -183,7 +180,6 @@ def load_schedule(name, fname, duration): | |||
| 183 | real_args = ['-w'] + real_args | 180 | real_args = ['-w'] + real_args |
| 184 | 181 | ||
| 185 | executables += [Executable(real_task, real_args)] | 182 | executables += [Executable(real_task, real_args)] |
| 186 | |||
| 187 | return proc_entries, executables | 183 | return proc_entries, executables |
| 188 | 184 | ||
| 189 | 185 | ||
| @@ -287,7 +283,7 @@ def run_experiment(data, start_message, ignore, jabber): | |||
| 287 | procs, execs = load_schedule(data.name, data.sched_file, data.params.duration) | 283 | procs, execs = load_schedule(data.name, data.sched_file, data.params.duration) |
| 288 | 284 | ||
| 289 | exp = Experiment(data.name, data.params.scheduler, work_dir, | 285 | exp = Experiment(data.name, data.params.scheduler, work_dir, |
| 290 | data.out_dir, procs, execs, data.params.tracers) | 286 | data.out_dir, procs, execs, data.params.tracers, data.params.file_params['num_tasks']) |
| 291 | 287 | ||
| 292 | exp.log(start_message) | 288 | exp.log(start_message) |
| 293 | 289 | ||
| @@ -484,7 +480,6 @@ def run_exps(exps, opts): | |||
| 484 | sys.stderr.write("Failed experiment %s\n" % exp.name) | 480 | sys.stderr.write("Failed experiment %s\n" % exp.name) |
| 485 | sys.stderr.write("%s\n" % e) | 481 | sys.stderr.write("%s\n" % e) |
| 486 | exp.state = ExpState.Failed | 482 | exp.state = ExpState.Failed |
| 487 | |||
| 488 | if exp.state is ExpState.Failed and opts.retry: | 483 | if exp.state is ExpState.Failed and opts.retry: |
| 489 | exps_remaining += [(i, exp)] | 484 | exps_remaining += [(i, exp)] |
| 490 | 485 | ||
