aboutsummaryrefslogtreecommitdiffstats
path: root/gen/generators.py
diff options
context:
space:
mode:
Diffstat (limited to 'gen/generators.py')
-rw-r--r--gen/generators.py257
1 files changed, 257 insertions, 0 deletions
diff --git a/gen/generators.py b/gen/generators.py
new file mode 100644
index 0000000..2fc77a7
--- /dev/null
+++ b/gen/generators.py
@@ -0,0 +1,257 @@
1from Cheetah.Template import Template
2from collections import namedtuple
3from common import get_config_option
4from config.config import DEFAULTS
5from gen.dp import DesignPointGenerator
6from parse.tuple_table import ColMap
7
8import gen.rv as rv
9import os
10import random
11import run.litmus_util as lu
12import schedcat.generator.tasks as tasks
13import shutil as sh
14
15NAMED_PERIODS = {
16 'harmonic' : rv.uniform_choice([25, 50, 100, 200]),
17 'uni-short' : rv.uniform_int( 3, 33),
18 'uni-moderate' : rv.uniform_int(10, 100),
19 'uni-long' : rv.uniform_int(50, 250),
20}
21
22NAMED_UTILIZATIONS = {
23 'uni-very-light': rv.uniform(0.0001, 0.001),
24 'uni-light' : rv.uniform(0.001, 0.1),
25 'uni-medium' : rv.uniform( 0.1, 0.4),
26 'uni-heavy' : rv.uniform( 0.5, 0.9),
27
28 'exp-light' : rv.exponential(0, 1, 0.10),
29 'exp-medium' : rv.exponential(0, 1, 0.25),
30 'exp-heavy' : rv.exponential(0, 1, 0.50),
31
32 'bimo-light' : rv.multimodal([(rv.uniform(0.001, 0.5), 8),
33 (rv.uniform( 0.5, 0.9), 1)]),
34 'bimo-medium' : rv.multimodal([(rv.uniform(0.001, 0.5), 6),
35 (rv.uniform( 0.5, 0.9), 3)]),
36 'bimo-heavy' : rv.multimodal([(rv.uniform(0.001, 0.5), 4),
37 (rv.uniform( 0.5, 0.9), 5)]),
38}
39
40# Cheetah templates for schedule files
41TP_CLUSTER = "plugins/C-EDF/cluster{$level}"
42TP_RM = """#if $release_master
43release_master{1}
44#end if"""
45TP_TBASE = """#for $t in $task_set
46{}$t.cost $t.period
47#end for"""
48TP_PART_TASK = TP_TBASE.format("-p $t.cpu ")
49TP_GLOB_TASK = TP_TBASE.format("")
50
51GenOption = namedtuple('GenOption', ['name', 'types', 'default', 'help'])
52
53class BaseGenerator(object):
54 '''Creates sporadic task sets with the most common Litmus options.'''
55 def __init__(self, name, templates, options, params):
56 self.options = self.__make_options() + options
57
58 self.__setup_params(params)
59
60 self.params = params
61 self.template = "\n".join([TP_RM] + templates)
62 self.name = name
63
64 def __make_options(self):
65 '''Return generic Litmus options.'''
66
67 # Guess defaults using the properties of this computer
68 cpus = lu.num_cpus()
69 try:
70 config = get_config_option("RELEASE_MASTER") and True
71 except:
72 config = False
73 release_master = list(set([False, config]))
74
75 list_types = [str, float, type([])]
76
77 return [GenOption('cpus', int, [cpus],
78 'Number of processors on target system.'),
79 GenOption('num_tasks', int, range(cpus, 5*cpus, cpus),
80 'Number of tasks per experiment.'),
81 GenOption('utils', list_types + NAMED_UTILIZATIONS.keys(),
82 ['uni-medium'],'Task utilization distributions.'),
83 GenOption('periods', list_types + NAMED_PERIODS.keys(),
84 ['harmonic'], 'Task period distributions.'),
85 GenOption('release_master', [True,False], release_master,
86 'Redirect release interrupts to a single CPU.'),
87 GenOption('duration', float, [30], 'Experiment duration.')]
88
89 def __create_dist(self, name, value, named_dists):
90 '''Attempt to create a distribution representing the data in @value.
91 If @value is a string, use it as a key for @named_dists.'''
92 name = "%s distribution" % name
93 # A list of values
94 if type(value) == type([]):
95 map(lambda x : self.__check_value(name, x, [float, int]), value)
96 return rv.uniform_choice(value)
97 elif type(value) in [float, int]:
98 return lambda : value
99 elif value in named_dists:
100 return named_dists[value]
101 else:
102 raise ValueError("Invalid %s value: %s" % (name, value))
103
104 def __create_exp(self, exp_params, out_dir):
105 '''Create a single experiment with @exp_params in @out_dir.'''
106 pdist = self.__create_dist('period',
107 exp_params['periods'],
108 NAMED_PERIODS)
109 udist = self.__create_dist('utilization',
110 exp_params['utils'],
111 NAMED_UTILIZATIONS)
112 tg = tasks.TaskGenerator(period=pdist, util=udist)
113
114 ts = []
115 tries = 0
116 while len(ts) != exp_params['num_tasks'] and tries < 5:
117 ts = tg.make_task_set(max_tasks = exp_params['num_tasks'])
118 tries += 1
119 if len(ts) != exp_params['num_tasks']:
120 print("Failed to create task set with parameters: %s" % exp_params)
121
122 self._customize(ts, exp_params)
123
124 sched_file = out_dir + "/" + DEFAULTS['sched_file']
125 with open(sched_file, 'wa') as f:
126 exp_params['task_set'] = ts
127 f.write(str(Template(self.template, searchList=[exp_params])))
128
129 del exp_params['task_set']
130 exp_params_file = out_dir + "/" + DEFAULTS['params_file']
131 with open(exp_params_file, 'wa') as f:
132 exp_params['scheduler'] = 'CEDF'
133 f.write(str(exp_params))
134
135 def __setup_params(self, params):
136 '''Set default parameter values and check that values are valid.'''
137 for option in self.options:
138 if option.name not in params:
139 params[option.name] = option.default
140 params[option.name] = self._check_value(option.name,
141 option.types,
142 params[option.name])
143 return params
144
145
146 def _check_value(self, name, types, val):
147 '''Raise an exception if the value of type of @val is not specified
148 in @types. Returns a copy of @val with strings converted to raw
149 Python types, if possible.'''
150 if types == float:
151 types = [float, int]
152 if type(types) != type([]):
153 types = [types]
154 if type(val) != type([]):
155 val = [val]
156
157 retval = []
158 for v in val:
159 # Has to be a better way to find this
160 v = False if v in ['f', 'False', 'false', 'n', 'no'] else v
161 v = True if v in ['t', 'True', 'true', 'y', 'yes'] else v
162
163 if type(v) not in types and v not in types:
164 # Try and convert v to one of the specified types
165 parsed = None
166 for t in types:
167 try:
168 parsed = t(v)
169 break
170 except:
171 pass
172
173 if parsed:
174 retval += [parsed]
175 else:
176 raise TypeError("Invalid %s value: '%s'" % (name, v))
177 else:
178 retval += [v]
179 return retval
180
181 def _customize(self, taskset, exp_params):
182 '''Configure a generated taskset with extra parameters.'''
183 pass
184
185 def create_exps(self, out_dir, force):
186 '''Create experiments for all possible combinations of params in
187 @out_dir. Overwrite existing files if @force is True.'''
188 col_map = ColMap()
189
190 # Track changing values so only relevant parameters are included
191 # in directory names
192 for dp in DesignPointGenerator(self.params):
193 for k, v in dp.iteritems():
194 col_map.try_add(k, v)
195
196 for dp in DesignPointGenerator(self.params):
197 dir_leaf = "sched=%s_%s" % (self.name, col_map.get_encoding(dp))
198 dir_path = "%s/%s" % (out_dir, dir_leaf.strip('_'))
199
200 if os.path.exists(dir_path):
201 if force:
202 sh.rmtree(dir_path)
203 else:
204 print("Skipping existing experiment: '%s'" % dir_path)
205 continue
206
207 os.mkdir(dir_path)
208
209 self.__create_exp(dp, dir_path)
210
211 def print_help(self):
212 s = str(Template("""Generator $name:
213 #for $o in $options
214 $o.name -- $o.help
215 \tDefault: $o.default
216 \tAllowed: $o.types
217 #end for""", searchList=vars(self)))
218
219 # Has to be an easier way to print this out...
220 for line in s.split("\n"):
221 res = []
222 i = 0
223 for word in line.split(", "):
224 i+= len(word)
225 res += [word]
226 if i > 80:
227 print ", ".join(res[:-1])
228 res = ["\t\t "+res[-1]]
229 i = line.index("'")
230 print ", ".join(res)
231
232class PartitionedGenerator(BaseGenerator):
233 def __init__(self, name, templates, options, params):
234 super(PartitionedGenerator, self).__init__(name,
235 templates + [TP_PART_TASK], options, params)
236
237 def _customize(self, taskset, exp_params):
238 start = 1 if exp_params['release_master'] else 0
239 # Random partition for now: could do a smart partitioning
240 for t in taskset:
241 t.cpu = random.randint(start, exp_params['cpus'] - 1)
242
243class PedfGenerator(PartitionedGenerator):
244 def __init__(self, params={}):
245 super(PedfGenerator, self).__init__("P-EDF", [], [], params)
246
247class CedfGenerator(PartitionedGenerator):
248 LEVEL_OPTION = GenOption('level', ['L2', 'L3', 'All'], ['L2'],
249 'Cache clustering level.',)
250
251 def __init__(self, params={}):
252 super(CedfGenerator, self).__init__("C-EDF", [TP_CLUSTER],
253 [CedfGenerator.LEVEL_OPTION], params)
254
255class GedfGenerator(BaseGenerator):
256 def __init__(self, params={}):
257 super(GedfGenerator, self).__init__("G-EDF", [TP_GLOB_TASK], [], params)