diff --git a/.vscode/.ropeproject/objectdb b/.vscode/.ropeproject/objectdb new file mode 100644 index 0000000..0a47446 Binary files /dev/null and b/.vscode/.ropeproject/objectdb differ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ae3a4b8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "simso", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/script.py", + "console": "internalConsole", + "justMyCode": false + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..aff3cbb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,27 @@ +{ + "python.pythonPath": "/usr/bin/python", + "editor.tokenColorCustomizations": { + "textMateRules": [ + { + "scope": "googletest.failed", + "settings": { + "foreground": "#f00" + } + }, + { + "scope": "googletest.passed", + "settings": { + "foreground": "#0f0" + } + }, + { + "scope": "googletest.run", + "settings": { + "foreground": "#0f0" + } + } + ] + }, + "python.linting.pylintEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/simso/core/Job.py b/simso/core/Job.py index 12afdbc..4f7f710 100644 --- a/simso/core/Job.py +++ b/simso/core/Job.py @@ -1,6 +1,7 @@ # coding=utf-8 -from SimPy.Simulation import Process, hold, passivate +# from SimPy.Simulation import Process, hold, passivate +from simpy import Process, Interrupt from simso.core.JobEvent import JobEvent from math import ceil @@ -27,7 +28,7 @@ class Job(Process): :type etm: AbstractExecutionTimeModel :type sim: Model """ - Process.__init__(self, name=name, sim=sim) + Process.__init__(self, env=sim, generator=self.activate_job()) # TODO: what to pass as generator? self._task = task self._pred = pred self.instr_count = 0 # Updated by the cache model. @@ -37,14 +38,15 @@ class Job(Process): self._start_date = None self._end_date = None self._is_preempted = False - self._activation_date = self.sim.now_ms() - self._absolute_deadline = self.sim.now_ms() + task.deadline self._aborted = False self._sim = sim + self._activation_date = self._sim.now_ms() + self._absolute_deadline = self._sim.now_ms() + task.deadline self._monitor = monitor self._etm = etm self._was_running_on = task.cpu self._wcet = task.wcet + self.name = name self._on_activate() @@ -62,7 +64,7 @@ class Job(Process): self._etm.on_activate(self) def _on_execute(self): - self._last_exec = self.sim.now() + self._last_exec = self._sim.now() self._etm.on_execute(self) if self._is_preempted: @@ -76,7 +78,7 @@ class Job(Process): def _on_stop_exec(self): if self._last_exec is not None: - self._computation_time += self.sim.now() - self._last_exec + self._computation_time += self._sim.now() - self._last_exec self._last_exec = None def _on_preempted(self): @@ -87,13 +89,13 @@ class Job(Process): self._monitor.observe(JobEvent(self, JobEvent.PREEMPTED)) self._sim.logger.log(self.name + " Preempted! ret: " + - str(self.interruptLeft), kernel=True) + str("Don't know what to pass else?"), kernel=True) # TODO: what to pass as interrupted? def _on_terminated(self): self._on_stop_exec() self._etm.on_terminated(self) - self._end_date = self.sim.now() + self._end_date = self._sim.now() self._monitor.observe(JobEvent(self, JobEvent.TERMINATED)) self._task.end_job(self) self._task.cpu.terminate(self) @@ -102,7 +104,7 @@ class Job(Process): def _on_abort(self): self._on_stop_exec() self._etm.on_abort(self) - self._end_date = self.sim.now() + self._end_date = self._sim.now() self._aborted = True self._monitor.observe(JobEvent(self, JobEvent.ABORTED)) self._task.end_job(self) @@ -181,7 +183,7 @@ class Job(Process): Dynamic laxity of the job in ms. """ return (self.absolute_deadline - self.ret - ) * self.sim.cycles_per_ms - self.sim.now() + ) * self._sim.cycles_per_ms - self._sim.now() @property def computation_time(self): @@ -199,7 +201,7 @@ class Job(Process): return int(self._computation_time) else: return (int(self._computation_time) + - self.sim.now() - self._last_exec) + self._sim.now() - self._last_exec) @property def actual_computation_time(self): @@ -288,39 +290,35 @@ class Job(Process): return self._pred def activate_job(self): - self._start_date = self.sim.now() + self._start_date = self._sim.now() # Notify the OS. self._task.cpu.activate(self) # While the job's execution is not finished. while self._end_date is None: # Wait an execute order. - yield passivate, self - - # Execute the job. - if not self.interrupted(): + try: + yield self._sim.event() + except Interrupt: + pass + else: self._on_execute() # ret is a duration lower than the remaining execution time. ret = self._etm.get_ret(self) while ret > 0: - yield hold, self, int(ceil(ret)) - - if not self.interrupted(): - # If executed without interruption for ret cycles. - ret = self._etm.get_ret(self) - else: + try: + yield self._sim.timeout(int(ceil(ret))) + except Interrupt: self._on_preempted() - self.interruptReset() break + else: + ret = self._etm.get_ret(self) if ret <= 0: # End of job. self._on_terminated() - else: - self.interruptReset() - class SequentialJob(Job): pass diff --git a/simso/core/Logger.py b/simso/core/Logger.py index 2631cf2..7c2efb2 100644 --- a/simso/core/Logger.py +++ b/simso/core/Logger.py @@ -1,7 +1,6 @@ # coding=utf-8 -from SimPy.Simulation import Monitor - +from simso.core.Monitor import Monitor class Logger(object): """ @@ -13,7 +12,7 @@ class Logger(object): - `sim`: The :class:`model ` object. """ self.sim = sim - self._logs = Monitor(name="Logs", sim=sim) + self._logs = Monitor(name="Logs", env=sim) def log(self, msg, kernel=False): """ diff --git a/simso/core/Model.py b/simso/core/Model.py index 3f58584..d56a774 100644 --- a/simso/core/Model.py +++ b/simso/core/Model.py @@ -1,16 +1,16 @@ # coding=utf-8 -from SimPy.Simulation import Simulation +# from simpy.Simulation import Simulation +from simpy import Environment from simso.core.Processor import Processor from simso.core.Task import Task from simso.core.Timer import Timer from simso.core.etm import execution_time_models from simso.core.Logger import Logger from simso.core.results import Results -import SimPy as SPPc -class Model(Simulation): +class Model(Environment): """ Main class for the simulation. It instantiate the various components required by the simulation and run it. @@ -27,8 +27,7 @@ class Model(Simulation): Methods: """ - print(SPPc.__version__) - Simulation.__init__(self) + Environment.__init__(self) self._logger = Logger(self) task_info_list = configuration.task_info_list proc_info_list = configuration.proc_info_list @@ -49,7 +48,7 @@ class Model(Simulation): # Init the processor class. This will in particular reinit the # identifiers to 0. Processor.init() - + # Initialization of the caches for cache in configuration.caches_list: cache.init() @@ -76,7 +75,7 @@ class Model(Simulation): self.results = None def now_ms(self): - return float(self.now()) / self._cycles_per_ms + return float(self.now) / self._cycles_per_ms @property def logs(self): @@ -127,25 +126,25 @@ class Model(Simulation): def _on_tick(self): if self._callback: - self._callback(self.now()) + self._callback(self.now) def run_model(self): """ Execute the simulation.""" - self.initialize() + # self.initialize() self.scheduler.init() self.progress.start() for cpu in self._processors: - self.activate(cpu, cpu.run()) + self.process(cpu.run()) for task in self._task_list: - self.activate(task, task.execute()) + self.process(task.execute()) try: - self.simulate(until=self._duration) + self.run(until=self._duration) finally: self._etm.update() - if self.now() > 0: + if self.now > 0: self.results = Results(self) self.results.end() diff --git a/simso/core/Monitor.py b/simso/core/Monitor.py new file mode 100644 index 0000000..fe61865 --- /dev/null +++ b/simso/core/Monitor.py @@ -0,0 +1,133 @@ +class Monitor(list): + """ Monitored variables + + A Class for monitored variables, that is, variables that allow one + to gather simple statistics. A Monitor is a subclass of list and + list operations can be performed on it. An object is established + using m = Monitor(name = '..'). It can be given a + unique name for use in debugging and in tracing and ylab and tlab + strings for labelling graphs. + """ + def __init__(self, env , name = 'a_Monitor', ylab = 'y', tlab = 't'): + list.__init__(self) + self.env = env + self.startTime = 0.0 + self.name = name + self.ylab = ylab + self.tlab = tlab + + def observe(self, y,t = None): + """record y and t""" + if t is None: t = self.env.now() + self.append([t, y]) + + def tally(self, y): + """ deprecated: tally for backward compatibility""" + self.observe(y, 0) + + def accum(self, y,t = None): + """ deprecated: accum for backward compatibility""" + self.observe(y, t) + + def reset(self, t = None): + """reset the sums and counts for the monitored variable """ + self[:] = [] + if t is None: t = self.env.now() + self.startTime = t + + def tseries(self): + """ the series of measured times""" + return list(zip(*self))[0] + + def yseries(self): + """ the series of measured values""" + return list(zip(*self))[1] + + def count(self): + """ deprecated: the number of observations made """ + return self.__len__() + + def total(self): + """ the sum of the y""" + if self.__len__() == 0: return 0 + else: + sum = 0.0 + for i in range(self.__len__()): + sum += self[i][1] + return sum # replace by sum() later + + def mean(self): + """ the simple average of the monitored variable""" + try: + return 1.0 * self.total() / self.__len__() + except ZeroDivisionError: + raise ZeroDivisionError('SimPy: No observations for mean') + + def var(self): + """ the sample variance of the monitored variable """ + n = len(self) + tot = self.total() + ssq = 0.0 + for i in range(self.__len__()): + ssq += self[i][1] ** 2 # replace by sum() eventually + try: + return (ssq - float(tot * tot) / n) / n + except: + raise ZeroDivisionError( + 'SimPy: No observations for sample variance') + + def timeAverage(self, t = None): + """ + The time-weighted average of the monitored variable. + + If t is used it is assumed to be the current time, + otherwise t = self.sim.now() + + """ + N = self.__len__() + if N == 0: + return None + + if t is None: t = self.env.now() + sum = 0.0 + tlast = self[0][0] + ylast = self[0][1] + for i in range(N): + ti, yi = self[i] + sum += ylast * (ti - tlast) + tlast = ti + ylast = yi + sum += ylast * (t - tlast) + T = t - self[0][0] + if T == 0: + return None + return sum / float(T) + + def timeVariance(self, t = None): + """ the time - weighted Variance of the monitored variable. + + If t is used it is assumed to be the current time, + otherwise t = self.sim.now() + """ + N = self.__len__() + if N == 0: + return None + if t is None: t = self.env.now() + sm = 0.0 + ssq = 0.0 + tlast = self[0][0] + # print 'DEBUG: 1 twVar ', t, tlast + ylast = self[0][1] + for i in range(N): + ti, yi = self[i] + sm += ylast * (ti - tlast) + ssq += ylast * ylast * (ti - tlast) + tlast = ti + ylast = yi + sm += ylast * (t - tlast) + ssq += ylast * ylast * (t - tlast) + T = t - self[0][0] + if T == 0: + return None + mn = sm / float(T) + return ssq / float(T) - mn * mn diff --git a/simso/core/Processor.py b/simso/core/Processor.py index 344eac4..8cd4dd0 100644 --- a/simso/core/Processor.py +++ b/simso/core/Processor.py @@ -1,10 +1,11 @@ # coding=utf-8 from collections import deque -from SimPy.Simulation import Process, Monitor, hold, waituntil +from simpy import Process +# from SimPy.Simulation import Process, Monitor, hold, waituntil from simso.core.ProcEvent import ProcRunEvent, ProcIdleEvent, \ ProcOverheadEvent, ProcCxtSaveEvent, ProcCxtLoadEvent - +from simso.core.Monitor import Monitor RESCHED = 1 ACTIVATE = 2 @@ -53,7 +54,7 @@ class Processor(Process): cls._identifier = 0 def __init__(self, model, proc_info): - Process.__init__(self, name=proc_info.name, sim=model) + Process.__init__(self, env=model, generator=self.run()) # TODO: what to pass as generator? self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 @@ -62,15 +63,14 @@ class Processor(Process): self.was_running = None self._evts = deque([]) self.sched = model.scheduler - self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) + self.monitor = Monitor(env=model, name="Monitor" + proc_info.name) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) - self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, - sim=model) + self.timer_monitor = Monitor(env=model, name="Monitor Timer" + proc_info.name) self._speed = proc_info.speed def resched(self): @@ -143,28 +143,32 @@ class Processor(Process): """ return self._running + def waituntil(self, cond_func, delay=1): + while not cond_func(): + yield self.env.timeout(delay) + def run(self): while True: if not self._evts: job = self._running if job: - yield waituntil, self, lambda: job.context_ok + yield self.waituntil(lambda: job.context_ok) self.monitor.observe(ProcCxtLoadEvent()) - yield hold, self, self.cl_overhead # overhead load context + yield self._model.timeout(self.cl_overhead) # overhead load context self.monitor.observe(ProcCxtLoadEvent(terminated=True)) job.interruptReset() - self.sim.reactivate(job) + self.env.reactivate(job) self.monitor.observe(ProcRunEvent(job)) job.context_ok = False else: self.monitor.observe(ProcIdleEvent()) # Wait event. - yield waituntil, self, lambda: self._evts + yield self.waituntil(lambda: self._evts) if job: self.interrupt(job) self.monitor.observe(ProcCxtSaveEvent()) - yield hold, self, self.cs_overhead # overhead save context + yield self._model.timeout(self.cs_overhead) # overhead save context self.monitor.observe(ProcCxtSaveEvent(terminated=True)) job.context_ok = True @@ -178,28 +182,28 @@ class Processor(Process): self.sched.on_activate(evt[1]) self.monitor.observe(ProcOverheadEvent("JobActivation")) self.sched.monitor_begin_activate(self) - yield hold, self, self.sched.overhead_activate + yield self._model.timeout(self.sched.overhead_activate) self.sched.monitor_end_activate(self) elif evt[0] == TERMINATE: self.sched.on_terminated(evt[1]) self.monitor.observe(ProcOverheadEvent("JobTermination")) self.sched.monitor_begin_terminate(self) - yield hold, self, self.sched.overhead_terminate + yield self._model.timeout(self.sched.overhead_terminate) self.sched.monitor_end_terminate(self) elif evt[0] == TIMER: self.timer_monitor.observe(None) if evt[1].overhead > 0: - print(self.sim.now(), "hold", evt[1].overhead) - yield hold, self, evt[1].overhead + print(self.env.now(), "hold", evt[1].overhead) + yield self._model.timeout(evt[1].overhead) evt[1].call_handler() elif evt[0] == SPEED: self._speed = evt[1] elif evt[0] == RESCHED: self.monitor.observe(ProcOverheadEvent("Scheduling")) self.sched.monitor_begin_schedule(self) - yield waituntil, self, self.sched.get_lock + yield self.waituntil(self.sched.get_lock) decisions = self.sched.schedule(self) - yield hold, self, self.sched.overhead # overhead scheduling + yield self._model.timeout(self.sched.overhead) # overhead scheduling if type(decisions) is not list: decisions = [decisions] decisions = [d for d in decisions if d is not None] diff --git a/simso/core/Scheduler.py b/simso/core/Scheduler.py index bdcd8c0..ad751c6 100644 --- a/simso/core/Scheduler.py +++ b/simso/core/Scheduler.py @@ -6,11 +6,12 @@ import importlib import pkgutil import inspect +from simso.core.Monitor import Monitor from simso.core.SchedulerEvent import SchedulerBeginScheduleEvent, \ SchedulerEndScheduleEvent, SchedulerBeginActivateEvent, \ SchedulerEndActivateEvent, SchedulerBeginTerminateEvent, \ SchedulerEndTerminateEvent -from SimPy.Simulation import Monitor + class SchedulerInfo(object): @@ -145,7 +146,7 @@ class Scheduler(object): self.overhead_activate = scheduler_info.overhead_activate self.overhead_terminate = scheduler_info.overhead_terminate self.data = scheduler_info.data - self.monitor = Monitor(name="MonitorScheduler", sim=sim) + self.monitor = Monitor(name="MonitorScheduler", env=sim) def init(self): """ diff --git a/simso/core/Task.py b/simso/core/Task.py index a37cfdf..b769e5d 100644 --- a/simso/core/Task.py +++ b/simso/core/Task.py @@ -1,9 +1,12 @@ # coding=utf-8 +import simpy +from simpy import Process from collections import deque -from SimPy.Simulation import Process, Monitor, hold, passivate +# from SimPy.Simulation import Process, Monitor, hold, passivate from simso.core.Job import Job from simso.core.Timer import Timer +from simso.core.Monitor import Monitor from .CSDP import CSDP import os @@ -129,11 +132,11 @@ class GenericTask(Process): :type sim: Model :type task_info: TaskInfo """ - Process.__init__(self, name=task_info.name, sim=sim) + Process.__init__(self, env=sim, generator=self.execute()) self.name = task_info.name self._task_info = task_info self._monitor = Monitor(name="Monitor" + self.name + "_states", - sim=sim) + env=sim) self._activations_fifo = deque([]) self._sim = sim self.cpu = None @@ -144,6 +147,9 @@ class GenericTask(Process): self._jobs = [] self.job = None + def execute(self): + pass + def __lt__(self, other): return self.identifier < other.identifier @@ -260,12 +266,12 @@ class GenericTask(Process): self._activations_fifo.popleft() if len(self._activations_fifo) > 0: self.job = self._activations_fifo[0] - self.sim.activate(self.job, self.job.activate_job()) + self.env.activate(self.job, self.job.activate_job()) def _job_killer(self, job): if job.end_date is None and job.computation_time < job.wcet: if self._task_info.abort_on_miss: - self.cancel(job) + # self.cancel(job) TODO: How to cancel Job? job.abort() def create_job(self, pred=None): @@ -275,15 +281,15 @@ class GenericTask(Process): """ self._job_count += 1 job = Job(self, "{}_{}".format(self.name, self._job_count), pred, - monitor=self._monitor, etm=self._etm, sim=self.sim) + monitor=self._monitor, etm=self._etm, sim=self.env) if len(self._activations_fifo) == 0: self.job = job - self.sim.activate(job, job.activate_job()) + self.env.process(job.activate_job()) self._activations_fifo.append(job) self._jobs.append(job) - timer_deadline = Timer(self.sim, GenericTask._job_killer, + timer_deadline = Timer(self.env, GenericTask._job_killer, (self, job), self.deadline) timer_deadline.start() @@ -301,7 +307,7 @@ class ATask(GenericTask): def execute(self): self._init() - yield passivate, self + yield self.env.event() class PTask(GenericTask): @@ -314,13 +320,13 @@ class PTask(GenericTask): def execute(self): self._init() # wait the activation date. - yield hold, self, int(self._task_info.activation_date * - self._sim.cycles_per_ms) + yield self.env.timeout(int(self._task_info.activation_date * + self._sim.cycles_per_ms)) while True: # print self.sim.now(), "activate", self.name self.create_job() - yield hold, self, int(self.period * self._sim.cycles_per_ms) + yield self.env.timeout(int(self.period * self._sim.cycles_per_ms)) class SporadicTask(GenericTask): @@ -334,8 +340,8 @@ class SporadicTask(GenericTask): self._init() for ndate in self.list_activation_dates: - yield hold, self, int(ndate * self._sim.cycles_per_ms) \ - - self._sim.now() + yield self.env.timeout(int(ndate * self._sim.cycles_per_ms) \ + - self._sim.now()) self.create_job() @property @@ -354,8 +360,8 @@ class ProbabilisticTask(GenericTask): self._init() for ndate in self.list_activation_dates: - yield hold, self, int(ndate * self._sim.cycles_per_ms) \ - - self._sim.now() + yield self.env.timeout(int(ndate * self._sim.cycles_per_ms) \ + - self._sim.now()) self.create_job() @property diff --git a/simso/core/Timer.py b/simso/core/Timer.py index f410e4d..3e4adc5 100644 --- a/simso/core/Timer.py +++ b/simso/core/Timer.py @@ -1,13 +1,14 @@ # coding=utf-8 -from SimPy.Simulation import Process, hold +# from SimPy.Simulation import Process, hold +from simpy import Environment, Process, Interrupt # TODO: allow the user to specify an overhead. class InstanceTimer(Process): def __init__(self, timer): - Process.__init__(self, name="Timer", sim=timer.sim) + Process.__init__(self, env=timer.env, generator=self.run()) self.function = timer.function self.args = timer.args self.delay = timer.delay @@ -23,15 +24,19 @@ class InstanceTimer(Process): def run(self): self.running = True while self.running: - yield hold, self, self.delay - if self.interrupted() or not self.running: + try: + yield self.env.timeout(self.delay) + except Interrupt: break - if self.cpu: - self.cpu.timer(self) else: - self.call_handler() - if self.one_shot: - break + if not self.running: + break; + if self.cpu: + self.cpu.timer(self) + else: + self.call_handler() + if self.one_shot: + break class Timer(object): @@ -67,7 +72,7 @@ class Timer(object): Methods: """ - self.sim = sim + self.env = sim self.function = function self.args = args if in_ms: @@ -89,7 +94,7 @@ class Timer(object): Start the timer. """ self.instance = InstanceTimer(self) - self.sim.activate(self.instance, self.instance.run(), self.prior) + self.env.process(self.instance.run()) # TODO: , self.prior what's with this? def stop(self): """ diff --git a/simso/core/__init__.py b/simso/core/__init__.py index e9eb171..9249506 100644 --- a/simso/core/__init__.py +++ b/simso/core/__init__.py @@ -8,4 +8,5 @@ from simso.core.Model import Model from simso.core.Processor import Processor from simso.core.Scheduler import Scheduler from simso.core.Timer import Timer -from simso.core.results import Results \ No newline at end of file +from simso.core.results import Results +from simso.core.Monitor import Monitor \ No newline at end of file