diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-05 14:24:33 -0400 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-15 11:42:33 -0400 |
commit | f659a11e1c888b01cce64fee5ae064a67aa4d777 (patch) | |
tree | 4e33cbaa54a697c119e5f30b1bf84fb22db94cba /gen/mc_generators.py | |
parent | 7cef798367892aff5ae76780b15c3a0f135d0234 (diff) |
Added ColorMcGenerator.
Diffstat (limited to 'gen/mc_generators.py')
-rw-r--r-- | gen/mc_generators.py | 206 |
1 files changed, 189 insertions, 17 deletions
diff --git a/gen/mc_generators.py b/gen/mc_generators.py index d6d8d90..bbb1ab9 100644 --- a/gen/mc_generators.py +++ b/gen/mc_generators.py | |||
@@ -1,5 +1,6 @@ | |||
1 | import gen.rv as rv | 1 | import gen.rv as rv |
2 | 2 | ||
3 | from color import get_cache_info,CacheInfo,BlockColorScheme,RandomColorScheme,EvilColorScheme | ||
3 | from common import try_get_config_option | 4 | from common import try_get_config_option |
4 | from gen.generator import GenOption,Generator,NAMED_UTILIZATIONS,NAMED_PERIODS | 5 | from gen.generator import GenOption,Generator,NAMED_UTILIZATIONS,NAMED_PERIODS |
5 | 6 | ||
@@ -16,11 +17,11 @@ TP_BASE = """#for $t in $lvl{0} | |||
16 | TP_LVLA = """#if $lvla | 17 | TP_LVLA = """#if $lvla |
17 | /proc/litmus/plugins/MC-CE/ce_file{ | 18 | /proc/litmus/plugins/MC-CE/ce_file{ |
18 | #for $t in $lvla | 19 | #for $t in $lvla |
19 | $t.cpu, $t.id, $t.budget | 20 | $t.cpu, $t.lvla_id, $t.budget |
20 | #end for | 21 | #end for |
21 | } | 22 | } |
22 | #end if | 23 | #end if |
23 | """ + TP_BASE.format("a", "-i $t.id -p $t.cpu ") | 24 | """ + TP_BASE.format("a", "-i $t.lvla_id -p $t.cpu ") |
24 | TP_LVLB = TP_BASE.format("b", "-p $t.cpu ") | 25 | TP_LVLB = TP_BASE.format("b", "-p $t.cpu ") |
25 | TP_LVLC = TP_BASE.format("c", "") | 26 | TP_LVLC = TP_BASE.format("c", "") |
26 | TP_LVLD = """#if $be | 27 | TP_LVLD = """#if $be |
@@ -46,10 +47,12 @@ MC_OPT = 'PLUGIN_MC' | |||
46 | LEVELS = 3 | 47 | LEVELS = 3 |
47 | 48 | ||
48 | class McGenerator(Generator): | 49 | class McGenerator(Generator): |
49 | def __init__(self, params = {}): | 50 | def __init__(self, name="MC", |
50 | super(McGenerator, self).__init__("MC", | 51 | templates=[TP_LVLA, TP_LVLB, TP_LVLC, TP_LVLD], |
51 | [TP_LVLA, TP_LVLB, TP_LVLC, TP_LVLD], | 52 | options=[], params={}): |
52 | self.__make_options(), | 53 | super(McGenerator, self).__init__(name, |
54 | templates, | ||
55 | self.__make_options() + options, | ||
53 | params) | 56 | params) |
54 | 57 | ||
55 | def __make_options(self): | 58 | def __make_options(self): |
@@ -92,34 +95,46 @@ class McGenerator(Generator): | |||
92 | 'Level-C task utilizations (at level C).'), | 95 | 'Level-C task utilizations (at level C).'), |
93 | 96 | ||
94 | Generator._dist_option('shares', ['fair'], NAMED_SHARES, | 97 | Generator._dist_option('shares', ['fair'], NAMED_SHARES, |
95 | 'Distribution of actual utilizations.')] | 98 | 'Distribution of runtime utilizations.')] |
96 | 99 | ||
97 | def __partition_worst_fit(self, params, ts): | 100 | def __partition_worst_fit(self, params, ts): |
101 | cpus = int(params['cpus']) | ||
102 | if params['release_master']: | ||
103 | # No level B on the release master | ||
104 | cpus -= 1 | ||
105 | |||
98 | # Partition using worst-fit for most even distribution | 106 | # Partition using worst-fit for most even distribution |
99 | utils = [0]*int(params['cpus']) | 107 | utils = [0]*cpus |
100 | tasks = [0]*int(params['cpus']) | 108 | tasks = [0]*cpus |
101 | for t in ts: | 109 | for t in ts: |
102 | t.cpu = utils.index(min(utils)) | 110 | t.cpu = utils.index(min(utils)) |
103 | t.id = tasks[t.cpu] | 111 | t.lvla_id = tasks[t.cpu] |
104 | 112 | ||
105 | utils[t.cpu] += t.utilization() | 113 | utils[t.cpu] += t.utilization() |
106 | tasks[t.cpu] += 1 | 114 | tasks[t.cpu] += 1 |
107 | 115 | ||
116 | # Increment by one so release master has no tasks | ||
117 | t.cpu += 1 | ||
118 | |||
108 | def __adjust(self, params, level): | 119 | def __adjust(self, params, level): |
109 | # Adjust for levels which aren't used | 120 | # Adjust for levels which aren't used |
110 | ldiff = LEVELS - params['levels'] | 121 | num = params['levels'] |
111 | shares = self.shares[ldiff:] | 122 | shares = list(self.shares) |
112 | level -= ldiff | 123 | if num < 4: |
124 | shares.pop() | ||
125 | if num < 3: | ||
126 | shares.pop(0) | ||
127 | level -= 1 | ||
113 | 128 | ||
114 | return shares, level | 129 | return shares, level |
115 | 130 | ||
116 | def __get_max_util(self, params, level): | 131 | def __get_max_util(self, params, level): |
117 | shares, level = self.__adjust(params, level) | 132 | shares, level = self.__adjust(params, level) |
118 | return float(shares[level]) / sum(shares[:level]) * params['cpus'] | 133 | return float(shares[level]) / sum(shares[:level+1]) * params['cpus'] |
119 | 134 | ||
120 | def __get_scale(self, params, level): | 135 | def __get_scale(self, params, level): |
121 | shares, level = self.__adjust(params, level) | 136 | shares, level = self.__adjust(params, level) |
122 | return float(shares[level]) / sum(shares) | 137 | return float(sum(shares[:level+1])) / sum(shares) |
123 | 138 | ||
124 | def __create_lvla_sched(self, params): | 139 | def __create_lvla_sched(self, params): |
125 | if params['levels'] < 3: | 140 | if params['levels'] < 3: |
@@ -148,11 +163,13 @@ class McGenerator(Generator): | |||
148 | utils = self._create_dist('utilization', params['b_utils'], | 163 | utils = self._create_dist('utilization', params['b_utils'], |
149 | NAMED_UTILIZATIONS) | 164 | NAMED_UTILIZATIONS) |
150 | 165 | ||
151 | # Level-A is present, b must be harmonic with lvla hyperperiod | 166 | |
152 | if params['levels'] > 2: | 167 | if params['levels'] > 2: |
168 | # Level-A is present, b must be harmonic with lvla hyperperiod | ||
153 | plist = [params['a_hyperperiod']*2**x for x in xrange(0, 4)] | 169 | plist = [params['a_hyperperiod']*2**x for x in xrange(0, 4)] |
154 | periods = rv.uniform_choice(plist) | 170 | periods = rv.uniform_choice(plist) |
155 | else: | 171 | else: |
172 | # Level b can have whatever periods it wants | ||
156 | periods = self._create_dist('period', params['b_periods'], | 173 | periods = self._create_dist('period', params['b_periods'], |
157 | NAMED_PERIODS) | 174 | NAMED_PERIODS) |
158 | max_util = self.__get_max_util(params, 1) | 175 | max_util = self.__get_max_util(params, 1) |
@@ -171,6 +188,9 @@ class McGenerator(Generator): | |||
171 | 188 | ||
172 | return self._create_taskset(params, periods, utils, max_util) | 189 | return self._create_taskset(params, periods, utils, max_util) |
173 | 190 | ||
191 | def _customize(self, task_system, params): | ||
192 | pass | ||
193 | |||
174 | def _create_exp(self, params): | 194 | def _create_exp(self, params): |
175 | # Ugly way of doing it | 195 | # Ugly way of doing it |
176 | self.shares = self._create_dist('shares', params['shares'], | 196 | self.shares = self._create_dist('shares', params['shares'], |
@@ -193,13 +213,165 @@ class McGenerator(Generator): | |||
193 | 213 | ||
194 | scales = [] | 214 | scales = [] |
195 | for index, level in enumerate('abc'): | 215 | for index, level in enumerate('abc'): |
196 | scales += [('scale%s' % level, self.__get_scale(params, index))] | 216 | if tasks['lvl%s'%level]: |
217 | scales += [('scale%s' % level, self.__get_scale(params, index))] | ||
197 | 218 | ||
198 | schedule_variables = params.items() + tasks.items() + scales | 219 | schedule_variables = params.items() + tasks.items() + scales |
199 | param_variables = params.items() + [('config-options',conf_options)] | 220 | param_variables = params.items() + [('config-options',conf_options)] |
200 | 221 | ||
222 | self._customize(tasks, params) | ||
223 | |||
201 | self._write_schedule(dict(schedule_variables)) | 224 | self._write_schedule(dict(schedule_variables)) |
202 | self._write_params(dict(param_variables)) | 225 | self._write_params(dict(param_variables)) |
203 | 226 | ||
204 | # Ugly | 227 | # Ugly |
205 | del(self.shares) | 228 | del(self.shares) |
229 | |||
230 | |||
231 | # Types are base, locking, preemptive | ||
232 | # This sets up the scheduler to create each | ||
233 | TP_TYPE = """#if $type != 'unmanaged' | ||
234 | /proc/sys/litmus/color/lock_cache{1} | ||
235 | #else | ||
236 | /proc/sys/litmus/color/lock_cache{0} | ||
237 | #end if | ||
238 | #if $type == 'scheduling' | ||
239 | /proc/sys/litmus/color/preempt_cache{1} | ||
240 | #else | ||
241 | /proc/sys/litmus/color/preempt_cache{0} | ||
242 | #end if""" | ||
243 | |||
244 | # Use special spin for color tasks | ||
245 | TP_COLOR_BASE = """colorspin -y $t.id -x $t.colorcsv """ | ||
246 | |||
247 | TP_COLOR_B = TP_BASE.format("b", TP_COLOR_BASE + "-p $t.cpu ") | ||
248 | TP_COLOR_C = TP_BASE.format("c", TP_COLOR_BASE) | ||
249 | |||
250 | # Not even sure job splitting is still possible | ||
251 | TP_CHUNK = """#if $chunk_size > 0 | ||
252 | /proc/sys/litmus/color/chunk_size{$chunk_size} | ||
253 | #end if""" | ||
254 | |||
255 | COLOR_TYPES = ['scheduling', 'locking', 'unmanaged'] | ||
256 | |||
257 | class ColorMcGenerator(McGenerator): | ||
258 | def __init__(self, params = {}): | ||
259 | super(ColorMcGenerator, self).__init__("COLOR-MC", | ||
260 | templates=[TP_TYPE, TP_CHUNK, TP_COLOR_B, TP_COLOR_C], | ||
261 | options=self.__make_options(), | ||
262 | params=self.__extend_params(params)) | ||
263 | |||
264 | def __extend_params(self, params): | ||
265 | '''Add in fixed mixed-criticality parameters.''' | ||
266 | params['levels'] = 2 | ||
267 | params['be'] = False | ||
268 | params['redirect'] = True | ||
269 | params['release_master'] = True | ||
270 | params['timer_merging'] = False | ||
271 | params['slack_stealing'] = False | ||
272 | |||
273 | # Set these just so they aren't displayed to the user | ||
274 | params['d_nice'] = False | ||
275 | params['d_fifo'] = False | ||
276 | params['a_hyperperiod'] = 0 | ||
277 | params['a_utils'] = 'bimo-light' | ||
278 | |||
279 | return params | ||
280 | |||
281 | def __make_system_info(self): | ||
282 | info = get_cache_info() | ||
283 | |||
284 | if not info: | ||
285 | # Pick something semi-reasonable. these will work (wastefully) on | ||
286 | # many machines. The plugin will pidgeon hole pages into these | ||
287 | # specific areas, so even if the cache which runs this code has | ||
288 | # more ways and/or colors than these, it will run as though these | ||
289 | # are its parameters. This is sufficient for most testing | ||
290 | ways = 8 | ||
291 | colors = 8 | ||
292 | page = 4096 | ||
293 | line = 64 | ||
294 | |||
295 | cache = ways * colors * page | ||
296 | sets = cache / (line * ways) | ||
297 | |||
298 | info = CacheInfo(cache, line=line, page=page, | ||
299 | ways=ways, sets=sets, colors=colors) | ||
300 | |||
301 | self.system = info | ||
302 | |||
303 | def __make_options(self): | ||
304 | self.__make_system_info() | ||
305 | |||
306 | return [GenOption('type', COLOR_TYPES, COLOR_TYPES, | ||
307 | 'Cache management type.'), | ||
308 | GenOption('chunk_size', float, [0], 'Chunk size.'), | ||
309 | GenOption('ways', int, [self.system.ways], 'Ways (associativity).'), | ||
310 | GenOption('colors', int, [self.system.colors], | ||
311 | 'System colors (cache size / ways).'), | ||
312 | GenOption('page_size', int, [self.system.page], | ||
313 | 'System page size.'), | ||
314 | GenOption('wss', [float, int], [.5], | ||
315 | 'Task working set sizes. Can be expressed as a fraction ' + | ||
316 | 'of the cache.'), | ||
317 | GenOption('align_unmanaged', [True, False], [True], | ||
318 | 'Place all working sets of unmanaged task systems in '+ | ||
319 | 'the same location, for maximum interference.')] | ||
320 | |||
321 | |||
322 | def __get_wss_pages(self, params): | ||
323 | '''Return the number of pages in a single task's working set.''' | ||
324 | cache_pages = params['ways'] * params['colors'] | ||
325 | |||
326 | wss = params['wss'] | ||
327 | if type(wss) == float and wss <= 1.0: | ||
328 | # Can express wss as fraction of total cache | ||
329 | pages = int(wss*cache_pages) | ||
330 | else: | ||
331 | if wss < params['page_size']: | ||
332 | raise Exception(('Cannot have working set (%d) smaller than ' | ||
333 | 'a page (%d).') % (wss, params['page_size'])) | ||
334 | |||
335 | pages = wss / params['page_size'] | ||
336 | |||
337 | if pages > cache_pages: | ||
338 | raise Exception('WSS (%d) larger than the cache!' % (wss)) | ||
339 | |||
340 | return pages | ||
341 | |||
342 | |||
343 | def __make_csv(self, task): | ||
344 | '''Write task.colors into a csv file, stored as task.colorcsv.''' | ||
345 | fname = 'colors%d.csv' % task.id | ||
346 | task.colorcsv = fname | ||
347 | |||
348 | with open(self._out_dir() + "/" + fname, 'w') as f: | ||
349 | for color, replicas in task.colors.iteritems(): | ||
350 | f.write("%d, %d\n" % (color, replicas)) | ||
351 | |||
352 | def _customize(self, task_system, params): | ||
353 | '''Add coloring properties to the mixed-criticality task system.''' | ||
354 | # Every task needs a unique id for coloring and wss walk order | ||
355 | all_tasks = [] | ||
356 | for level, tasks in task_system.iteritems(): | ||
357 | all_tasks += tasks | ||
358 | for i, task in enumerate(all_tasks): | ||
359 | task.id = i | ||
360 | |||
361 | c = params['colors'] | ||
362 | w = params['ways'] | ||
363 | |||
364 | if params['type'] == 'unmanaged': | ||
365 | hrt_colorer = EvilColorScheme(c, w) | ||
366 | srt_colorer = hrt_colorer | ||
367 | else: | ||
368 | srt_colorer = RandomColorScheme(c, w) | ||
369 | hrt_colorer = BlockColorScheme(c, w, way_first=True) | ||
370 | |||
371 | pages_needed = self.__get_wss_pages(params) | ||
372 | |||
373 | hrt_colorer.color(task_system['lvlb'], pages_needed) | ||
374 | srt_colorer.color(task_system['lvlc'], pages_needed) | ||
375 | |||
376 | for t in all_tasks: | ||
377 | self.__make_csv(t) | ||