Scheduler.py 8.96 KB
Newer Older
1 2 3 4
from __future__ import print_function
import sys
import imp
import os.path
5 6 7
import importlib
import pkgutil
import inspect
8

9
from simso.core.Monitor import Monitor
10 11 12 13
from simso.core.SchedulerEvent import SchedulerBeginScheduleEvent, \
    SchedulerEndScheduleEvent, SchedulerBeginActivateEvent, \
    SchedulerEndActivateEvent, SchedulerBeginTerminateEvent, \
    SchedulerEndTerminateEvent
14

15 16 17 18 19 20 21


class SchedulerInfo(object):
    """
    SchedulerInfo groups the data that characterize a Scheduler (such as the
    scheduling overhead) and do the dynamic loading of the scheduler.
    """
22
    def __init__(self, clas='', overhead=0, overhead_activate=0,
23 24 25 26 27 28 29 30 31
                 overhead_terminate=0, fields=None):
        """
        Args:
            - `name`: Name of the scheduler.
            - `cls`: Class associated to this scheduler.
            - `overhead`: Overhead associated to a scheduling decision.

        Methods:
        """
32 33
        self.filename = ''
        self.clas = clas
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
        self.overhead = overhead
        self.overhead_activate = overhead_activate
        self.overhead_terminate = overhead_terminate
        self.data = {}
        self.fields_types = {}

        if fields:
            for key, value in fields.items():
                self.data[key] = value[0]
                self.fields_types[key] = value[1]

    def set_fields(self, fields):
        for key, value in fields.items():
            self.data[key] = value[0]
            self.fields_types[key] = value[1]

    def get_cls(self):
        """
        Get the class of this scheduler.
        """
54 55 56
        try:
            clas = None
            if self.clas:
57 58 59 60 61 62
                if type(self.clas) is type:
                    clas = self.clas
                else:
                    name = self.clas.rsplit('.', 1)[1]
                    importlib.import_module(self.clas)
                    clas = getattr(importlib.import_module(self.clas), name)
63 64 65
            elif self.filename:
                path, name = os.path.split(self.filename)
                name = os.path.splitext(name)[0]
66 67 68 69

                fp, pathname, description = imp.find_module(name, [path])
                if path not in sys.path:
                    sys.path.append(path)
70 71
                clas = getattr(imp.load_module(name, fp, pathname,
                                               description), name)
72
                fp.close()
73 74 75 76 77 78 79 80 81

            return clas
        except Exception as e:
            print("ImportError: ", e)
            if self.clas:
                print("Class: {}".format(self.clas))
            else:
                print("Path: {}".format(self.filename))
            return None
82 83 84 85 86 87 88 89 90

    def instantiate(self, model):
        """
        Instantiate the :class:`Scheduler` class.

        Args:
            - `model`: The :class:`Model <simso.core.Model.Model>` object \
            that is passed to the constructor.
        """
91 92 93
        clas = self.get_cls()
        if clas:
            return clas(model, self)
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132


class Scheduler(object):
    """
    The implementation of a scheduler is done by subclassing this abstract
    class.

    The scheduling events are modeled by method calls which take as arguments
    the :class:`jobs <simso.core.Job.Job>` and the :class:`processors
    <simso.core.Processor.Processor>`.

    The following methods should be redefined in order to interact with the
    simulation:

        - :meth:`init` Called when the simulation is ready. The scheduler \
        logic should be initialized here.
        - :meth:`on_activate` Called upon a job activation.
        - :meth:`on_terminated` Called when a job is terminated.
        - :meth:`schedule` Take the scheduling decision. This method should \
        not be called directly. A call to the :meth:`resched \
        <simso.core.Processor.Processor.resched>` method is required.

    By default, the scheduler can only run on a single processor at the same
    simulation time. It is also possible to override this behavior by
    overriding the :meth:`get_lock` and :meth:`release_lock` methods.
    """

    def __init__(self, sim, scheduler_info, **kwargs):
        """
        Args:

        - `sim`: :class:`Model <simso.core.Model>` instance.
        - `scheduler_info`: A :class:`SchedulerInfo` representing the \
            scheduler.

        Attributes:

        - **sim**: :class:`Model <simso.core.Model.Model>` instance. \
            Useful to get current time with ``sim.now_ms()`` (in ms) or \
133
            ``sim.now`` (in cycles).
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
        - **processors**: List of :class:`processors \
            <simso.core.Processor.Processor>` handled by this scheduler.
        - **task_list**: List of :class:`tasks <simso.core.Task.GenericTask>` \
            handled by this scheduler.

        Methods:
        """
        self.sim = sim
        self.processors = []
        self.task_list = []
        self._lock = False
        self.overhead = scheduler_info.overhead
        self.overhead_activate = scheduler_info.overhead_activate
        self.overhead_terminate = scheduler_info.overhead_terminate
        self.data = scheduler_info.data
149
        self.monitor = Monitor(name="MonitorScheduler", env=sim)
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

    def init(self):
        """
        This method is called when the system is ready to run. This method
        should be used in lieu of the __init__ method. This method is
        guaranteed to be called when the simulation starts, after the tasks
        are instantiated
        """
        pass

    def on_activate(self, job):
        """
        This method is called upon a job activation.

        Args:
            - `job`: The activated :class:`job <simso.core.Job.Job>`.
        """
        pass

    def on_terminated(self, job):
        """
        This method is called when a job finish (termination or abortion).

        Args:
            - `job`: The :class:`job <simso.core.Job.Job>` that terminates .
        """
        pass

    def schedule(self, cpu):
        """
        The schedule method must be redefined by the simulated scheduler.
        It takes as argument the cpu on which the scheduler runs.

        Args:
            - `cpu`: The :class:`processor <simso.core.Processor.Processor>` \
            on which the scheduler runs.

        Returns a decision or a list of decisions. A decision is a couple
        (job, cpu).
        """
        raise NotImplementedError("Function schedule to override!")

    def add_task(self, task):
        """
        Add a task to the list of tasks handled by this scheduler.

        Args:
            - `task`: The :class:`task <simso.core.Task.GenericTask>` to add.
        """
        self.task_list.append(task)

    def add_processor(self, cpu):
        """
        Add a processor to the list of processors handled by this scheduler.

        Args:
            - `processor`: The :class:`processor \
            <simso.core.Processor.Processor>` to add.
        """
        self.processors.append(cpu)

    def get_lock(self):
        """
        Implement a lock mechanism. Override it to remove the lock or change
        its behavior.
        """
        if not self._lock:
            self._lock = True
        else:
            return False
        return True

    def release_lock(self):
        """
        Release the lock. Goes in pair with :meth:`get_lock`.
        """
        self._lock = False

    def monitor_begin_schedule(self, cpu):
        self.monitor.observe(SchedulerBeginScheduleEvent(cpu))

    def monitor_end_schedule(self, cpu):
        self.monitor.observe(SchedulerEndScheduleEvent(cpu))

    def monitor_begin_activate(self, cpu):
        self.monitor.observe(SchedulerBeginActivateEvent(cpu))

    def monitor_end_activate(self, cpu):
        self.monitor.observe(SchedulerEndActivateEvent(cpu))

    def monitor_begin_terminate(self, cpu):
        self.monitor.observe(SchedulerBeginTerminateEvent(cpu))

    def monitor_end_terminate(self, cpu):
        self.monitor.observe(SchedulerEndTerminateEvent(cpu))
245 246 247


def get_schedulers():
248 249 250 251 252 253 254 255 256
    modules = []

    # Special case when using PyInstaller:
    if getattr(sys, 'frozen', False):
        import pyi_importers
        importer = None
        for obj in sys.meta_path:
            if isinstance(obj, pyi_importers.FrozenImporter):
                importer = obj
257
                break
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281

        for name in importer.toc:
            if name.startswith('simso.schedulers.'):
                modules.append(name)

    # Normal case:
    else:
        package = importlib.import_module('simso.schedulers')
        for importer, modname, ispkg in pkgutil.walk_packages(
                path=package.__path__,
                prefix=package.__name__ + '.',
                onerror=lambda x: None):
            modules.append(modname)

    for modname in sorted(modules):
        try:
            m = importlib.import_module(modname)
            for name in dir(m):
                c = m.__getattribute__(name)
                if inspect.isclass(c) and issubclass(c, Scheduler):
                    yield modname
                    break
        except (ImportError, SyntaxError):
            print("Import error: ", modname)