aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-04-05 14:24:33 -0400
committerJonathan Herman <hermanjl@cs.unc.edu>2013-04-15 11:42:33 -0400
commitf659a11e1c888b01cce64fee5ae064a67aa4d777 (patch)
tree4e33cbaa54a697c119e5f30b1bf84fb22db94cba
parent7cef798367892aff5ae76780b15c3a0f135d0234 (diff)
Added ColorMcGenerator.
-rw-r--r--gen/__init__.py3
-rw-r--r--gen/color.py102
-rw-r--r--gen/generator.py35
-rw-r--r--gen/mc_generators.py206
4 files changed, 316 insertions, 30 deletions
diff --git a/gen/__init__.py b/gen/__init__.py
index 8c60b46..803bb37 100644
--- a/gen/__init__.py
+++ b/gen/__init__.py
@@ -1,6 +1,9 @@
1import generator as gen 1import generator as gen
2import edf_generators as edf 2import edf_generators as edf
3import mc_generators as mc
3 4
4gen.register_generator("G-EDF", edf.GedfGenerator) 5gen.register_generator("G-EDF", edf.GedfGenerator)
5gen.register_generator("P-EDF", edf.PedfGenerator) 6gen.register_generator("P-EDF", edf.PedfGenerator)
6gen.register_generator("C-EDF", edf.CedfGenerator) 7gen.register_generator("C-EDF", edf.CedfGenerator)
8gen.register_generator("MC", mc.McGenerator)
9gen.register_generator("Color-MC", mc.ColorMcGenerator)
diff --git a/gen/color.py b/gen/color.py
new file mode 100644
index 0000000..8184b8b
--- /dev/null
+++ b/gen/color.py
@@ -0,0 +1,102 @@
1import os
2import re
3
4from collections import namedtuple,defaultdict
5from math import ceil
6from random import randint
7
8class ColorScheme(object):
9 def __init__(self, colors, ways):
10 self.colors = colors
11 self.ways = ways
12
13 def color(self, tasks, wss):
14 '''Assign a color->replicas dict to each task in tasks.'''
15 raise NotImplementedError
16
17class BlockColorScheme(ColorScheme):
18 def __init__(self, colors, ways, way_first):
19 super(BlockColorScheme, self).__init__(colors, ways)
20 self.way_first = way_first
21
22 def color(self, tasks, pages_needed):
23 '''Pages are assigned in blocks, either maximizing the number of ways
24 or maximizing the number of colors used.'''
25 cpus = defaultdict(list)
26 for t in tasks:
27 cpus[t.cpu].append(t)
28
29 if self.way_first:
30 # Way first means maximize ways
31 pages_per_color = min(self.ways, pages_needed)
32 colors_per_task = int(ceil(pages_needed/pages_per_color))
33 else:
34 # Color first means maximize colors
35 colors_per_task = min(self.colors, pages_needed)
36 pages_per_color = int(ceil(pages_needed/colors_per_task))
37
38 curr_color = 0
39 for cpu, tasks in cpus.iteritems():
40 # All tasks on a CPU have the same coloring scheme
41 cpu_colors = defaultdict(int)
42 for _ in xrange(colors_per_task):
43 curr_color = (curr_color + 1) % self.colors
44 cpu_colors[curr_color] = pages_per_color
45
46 for t in tasks:
47 t.colors = cpu_colors
48
49class RandomColorScheme(ColorScheme):
50 def color(self, tasks, pages_needed):
51 '''Pages are placed randomly in the cache'''
52 if pages_needed >= self.ways * self.colors:
53 raise Exception("Too many pages: %d > %d * %d" %
54 (pages_needed, self.ways, self.colors))
55
56 for t in tasks:
57 t.colors = defaultdict(int)
58
59 for _ in xrange(pages_needed):
60 # Find the next color with available ways
61 while True:
62 next_color = randint(0, self.colors - 1)
63 if t.colors[next_color] != self.ways:
64 break
65
66 t.colors[next_color] += 1;
67
68class EvilColorScheme(ColorScheme):
69 def color(self, tasks, pages_needed):
70 '''All tasks' working sets are placed at the front of the cache'''
71 colors = defaultdict(int)
72 color = 0
73
74 while pages_needed > 0:
75 colors[color] = min(self.ways, pages_needed)
76 pages_needed -= colors[color]
77
78 color += 1
79
80 for t in tasks:
81 t.colors = colors
82
83
84INFO_FIELDS = ['cache', 'line', 'page', 'ways', 'sets', 'colors']
85INFO_PROC = '/proc/sys/litmus/color/cache_info'
86
87# Build parsing regex
88FIELD_REGEX = r"(?:.*?{0}.*?(?P<{0}>\d+).*?)"
89INFO_REGEX = "|".join([FIELD_REGEX.format(field) for field in INFO_FIELDS])
90INFO_REGEX = r"(?:{})*".format(INFO_REGEX)
91
92# To fill up this
93CacheInfo = namedtuple('CacheInfo', INFO_FIELDS)
94
95def get_cache_info():
96 if os.path.exists(INFO_PROC):
97 with open(INFO_PROC, 'r') as f:
98 data = f.read()
99 values = re.search(INFO_REGEX, data, re.M|re.I|re.S).groupdict()
100 return CacheInfo(**values)
101 else:
102 return None
diff --git a/gen/generator.py b/gen/generator.py
index f35a22b..1205490 100644
--- a/gen/generator.py
+++ b/gen/generator.py
@@ -51,38 +51,44 @@ class Generator(object):
51 This class also performs checks of parameter values and prints out help. 51 This class also performs checks of parameter values and prints out help.
52 All subclasses must implement _create_exp. 52 All subclasses must implement _create_exp.
53 ''' 53 '''
54 def __init__(self, scheduler, templates, options, params): 54 def __init__(self, name, templates, options, params):
55 self.__make_defaults(params)
56
55 self.options = self.__make_options(params) + options 57 self.options = self.__make_options(params) + options
56 58
57 self.__setup_params(params) 59 self.__setup_params(params)
58 60
59 self.params = params 61 self.params = params
60 self.template = "\n".join([TP_RM] + templates) 62 self.template = "\n".join([TP_RM] + templates)
61 self.scheduler = scheduler 63 self.scheduler = name
62
63 def __make_options(self, params):
64 '''Return generic Litmus options.'''
65 64
66 # Guess defaults using the properties of this computer 65 def __make_defaults(self, params):
66 '''Guess defaults using the properties of this computer'''
67 if 'cpus' in params: 67 if 'cpus' in params:
68 cpus = min(map(int, params['cpus'])) 68 self.cpus = min(map(int, params['cpus']))
69 else: 69 else:
70 cpus = num_cpus() 70 self.cpus = num_cpus()
71 try: 71 try:
72 config = get_config_option("RELEASE_MASTER") and True 72 config = get_config_option("RELEASE_MASTER") and True
73 except: 73 except:
74 config = False 74 config = False
75 release_master = list(set([False, config])) 75 self.release_master = list(set([False, config]))
76 76
77 77
78 return [GenOption('tasks', int, range(cpus, 5*cpus, cpus), 78 def __make_options(self, params):
79 'Number of tasks per experiment.'), 79 '''Return generic Litmus options.'''
80 GenOption('cpus', int, [cpus], 80 return [GenOption('num_tasks', int,
81 range(self.cpus, 5*self.cpus, self.cpus),
82 'Number of tasks per experiment.'),
83 GenOption('cpus', int, [self.cpus],
81 'Number of processors on target system.'), 84 'Number of processors on target system.'),
82 GenOption('release_master', [True,False], release_master, 85 GenOption('release_master', [True,False], self.release_master,
83 'Redirect release interrupts to a single CPU.'), 86 'Redirect release interrupts to a single CPU.'),
84 GenOption('duration', float, [30], 'Experiment duration.')] 87 GenOption('duration', float, [30], 'Experiment duration.')]
85 88
89 def _num_cpus(self):
90 return self.cpus
91
86 @staticmethod 92 @staticmethod
87 def _dist_option(name, default, distribution, help): 93 def _dist_option(name, default, distribution, help):
88 return GenOption(name, [str, float, type([])] + distribution.keys(), 94 return GenOption(name, [str, float, type([])] + distribution.keys(),
@@ -119,6 +125,9 @@ class Generator(object):
119 max_util) 125 max_util)
120 return ts 126 return ts
121 127
128 def _out_dir(self):
129 return self.out_dir
130
122 def _write_schedule(self, params): 131 def _write_schedule(self, params):
123 '''Write schedule file using current template for @params.''' 132 '''Write schedule file using current template for @params.'''
124 sched_file = self.out_dir + "/" + DEFAULTS['sched_file'] 133 sched_file = self.out_dir + "/" + DEFAULTS['sched_file']
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 @@
1import gen.rv as rv 1import gen.rv as rv
2 2
3from color import get_cache_info,CacheInfo,BlockColorScheme,RandomColorScheme,EvilColorScheme
3from common import try_get_config_option 4from common import try_get_config_option
4from gen.generator import GenOption,Generator,NAMED_UTILIZATIONS,NAMED_PERIODS 5from gen.generator import GenOption,Generator,NAMED_UTILIZATIONS,NAMED_PERIODS
5 6
@@ -16,11 +17,11 @@ TP_BASE = """#for $t in $lvl{0}
16TP_LVLA = """#if $lvla 17TP_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 ")
24TP_LVLB = TP_BASE.format("b", "-p $t.cpu ") 25TP_LVLB = TP_BASE.format("b", "-p $t.cpu ")
25TP_LVLC = TP_BASE.format("c", "") 26TP_LVLC = TP_BASE.format("c", "")
26TP_LVLD = """#if $be 27TP_LVLD = """#if $be
@@ -46,10 +47,12 @@ MC_OPT = 'PLUGIN_MC'
46LEVELS = 3 47LEVELS = 3
47 48
48class McGenerator(Generator): 49class 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
233TP_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
245TP_COLOR_BASE = """colorspin -y $t.id -x $t.colorcsv """
246
247TP_COLOR_B = TP_BASE.format("b", TP_COLOR_BASE + "-p $t.cpu ")
248TP_COLOR_C = TP_BASE.format("c", TP_COLOR_BASE)
249
250# Not even sure job splitting is still possible
251TP_CHUNK = """#if $chunk_size > 0
252/proc/sys/litmus/color/chunk_size{$chunk_size}
253#end if"""
254
255COLOR_TYPES = ['scheduling', 'locking', 'unmanaged']
256
257class 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)