From a812822977da46dd7a0ded1fd429120ed005edc0 Mon Sep 17 00:00:00 2001 From: Slim Ben Amor Date: Wed, 19 Dec 2018 11:00:16 +0100 Subject: [PATCH] Add probabilistic WCET feature The aim of this commit is to add new feature for real time tasks simulator 'simso'. We allow WCET of a task to be defined as a discrete Probability Distribution Function (PDF) using a matrix (numpy array) of two row: one for possible WCET values and the other for their corresponding probabilities. When the WCET of a task is defined as probability distribution, the simulator will pick random value from the possible WCET values proportionally to their probabilities. Then, it create a new job that has as WCET the generated random value. The modified files in this commit are: - simso/utils/probabilistic_calc.py: new file contain function that generate random value according to a probability distribution. - simso/configuration/Configuration.py: In case of probabilistic WCET, verify that all WCET values are positive and that probabilities sum to one. - simso/core/Job.py: define new attribute '_wcet' individual value for each job instance. - simso/core/Task.py: In case of probabilistic WCET, generate random value of WCET using 'randon_int_from_distr' function from 'probabilistic_calc.py' module. Then, create new job with the generated WCET. - simso/core/__init__.py: change import order to avoid program crush. misc/script.py file is also slightly modified to add a task 'T1' with probabilistic WCET. When run, this script will generate a scheduling where several instance of task 'T1' has different WCET values. --- misc/script.py | 3 +- simso/configuration/Configuration.py | 29 ++++++++++------- simso/core/Job.py | 9 ++++-- simso/core/Task.py | 12 +++++-- simso/core/__init__.py | 2 +- simso/utils/probabilistic_calc.py | 48 ++++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 simso/utils/probabilistic_calc.py diff --git a/misc/script.py b/misc/script.py index 6152855..754e885 100755 --- a/misc/script.py +++ b/misc/script.py @@ -7,6 +7,7 @@ import sys from simso.core import Model from simso.configuration import Configuration +import numpy as np def main(argv): @@ -21,7 +22,7 @@ def main(argv): # Add tasks: configuration.add_task(name="T1", identifier=1, period=7, - activation_date=0, wcet=3, deadline=7) + activation_date=0, wcet=np.array([[1,2,3],[.1,.2,.7]]), deadline=7) # add a task with a probabilistic WCET configuration.add_task(name="T2", identifier=2, period=12, activation_date=0, wcet=3, deadline=12) configuration.add_task(name="T3", identifier=3, period=20, diff --git a/simso/configuration/Configuration.py b/simso/configuration/Configuration.py index 4c0f2d7..ab1f5a3 100644 --- a/simso/configuration/Configuration.py +++ b/simso/configuration/Configuration.py @@ -4,6 +4,7 @@ import os import re from xml.dom import minidom +import numpy as np from simso.core.Scheduler import SchedulerInfo from simso.core import Scheduler from simso.core.Task import TaskInfo @@ -181,22 +182,33 @@ def check_tasks(self): "name must begins with a letter and must not contains any "\ "special character." - # Activation date >= 0: + # Activation date >= 0: assert task.activation_date >= 0, \ "Activation date must be positive." # Period >= 0: assert task.period >= 0, "Tasks' periods must be positives." - # Deadline >= 0: + # Deadline >= 0: assert task.deadline >= 0, "Tasks' deadlines must be positives." # N_instr >= 0: assert task.n_instr >= 0, \ "A number of instructions must be positive." - # WCET >= 0: - assert task.wcet >= 0, "WCET must be positive." + # WCET >= 0: + if isinstance(task.wcet, np.ndarray): + assert np.min(task.wcet[0, :]) >= 0, "WCETs must be positive" + assert np.sum(task.wcet[1, :]) == 1, "probabilities sum must be 1" + else: + assert task.wcet >= 0, "WCET must be positive." + if self.etm == "cache": + # stack + assert task.stack_file, "A task needs a stack profile." + + # stack ok + assert task.csdp, "Stack not found or empty." + # ACET >= 0: assert task.acet >= 0, "ACET must be positive." @@ -209,13 +221,6 @@ def check_tasks(self): assert 0.0 <= task.mix <= 2.0, \ "A mix must be positive and less or equal than 2.0" - if self.etm == "cache": - # stack - assert task.stack_file, "A task needs a stack profile." - - # stack ok - assert task.csdp, "Stack not found or empty." - def check_caches(self): for index, cache in enumerate(self._caches_list): # Id unique : @@ -231,7 +236,7 @@ def check_caches(self): # Taille positive : assert cache.size >= 0, "A cache size must be positive." - # Access time >= 0: + # Access time >= 0: assert cache.access_time >= 0, "An access time must be positive." def get_hyperperiod(self): diff --git a/simso/core/Job.py b/simso/core/Job.py index 4708fd3..b152384 100644 --- a/simso/core/Job.py +++ b/simso/core/Job.py @@ -9,7 +9,7 @@ class Job(Process): """The Job class simulate the behavior of a real Job. This *should* only be instantiated by a Task.""" - def __init__(self, task, name, pred, monitor, etm, sim): + def __init__(self, task, name, pred, monitor, etm, sim, wcet=None): """ Args: - `task`: The parent :class:`task `. @@ -44,6 +44,7 @@ def __init__(self, task, name, pred, monitor, etm, sim): self._monitor = monitor self._etm = etm self._was_running_on = task.cpu + self._wcet = wcet self._on_activate() @@ -243,7 +244,9 @@ def wcet(self): Worst-Case Execution Time in milliseconds. Equivalent to ``self.task.wcet``. """ - return self._task.wcet + if (self._wcet is None): + return self._task.wcet + return self._wcet @property def activation_date(self): @@ -291,7 +294,7 @@ def activate_job(self): # Wait an execute order. yield passivate, self - # Execute the job. + # Execute the job. if not self.interrupted(): self._on_execute() # ret is a duration lower than the remaining execution time. diff --git a/simso/core/Task.py b/simso/core/Task.py index da5ad2d..3316389 100644 --- a/simso/core/Task.py +++ b/simso/core/Task.py @@ -2,8 +2,10 @@ from collections import deque from SimPy.Simulation import Process, Monitor, hold, passivate +import numpy as np from simso.core.Job import Job from simso.core.Timer import Timer +from simso.utils.probabilistic_calc import random_int_from_distr from .CSDP import CSDP import os @@ -272,9 +274,15 @@ def create_job(self, pred=None): directly by a scheduler. """ self._job_count += 1 - job = Job(self, "{}_{}".format(self.name, self._job_count), pred, - monitor=self._monitor, etm=self._etm, sim=self.sim) + # pick random wcet according to their probabilities + if isinstance(self.wcet, np.ndarray): + wcet_sample = random_int_from_distr(self.wcet, n_sample=1)[0] + else: + wcet_sample = None + + job = Job(self, "{}_{}".format(self.name, self._job_count), pred, + monitor=self._monitor, etm=self._etm, sim=self.sim, wcet=wcet_sample) if len(self._activations_fifo) == 0: self.job = job self.sim.activate(job, job.activate_job()) diff --git a/simso/core/__init__.py b/simso/core/__init__.py index e9eb171..e5e2071 100644 --- a/simso/core/__init__.py +++ b/simso/core/__init__.py @@ -2,10 +2,10 @@ The core module include all the classes needed for the simulation. """ +from simso.core.Scheduler import Scheduler from simso.core.JobEvent import JobEvent from simso.core.ProcEvent import ProcEvent 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 diff --git a/simso/utils/probabilistic_calc.py b/simso/utils/probabilistic_calc.py new file mode 100644 index 0000000..8174616 --- /dev/null +++ b/simso/utils/probabilistic_calc.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Tue Nov 27 17:23:01 2018 + +@author: sbenamor +""" + +from random import random +import numpy as np + + +def random_int_from_distr(proba_dist, n_sample=1): + """ function that generate n random value according to a given + discrete probability distribution proba_dist + + Args: + - proba_dist (numpy array 2*m): discrete probability distribution with m possible value. + - n_sample (int): number of generated samples. + + Returns: + - list: list of n random generated values. + + Example: + >>> randon_int_distr(np.array([[5],[1]]),1) #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + [5] + >>> randon_int_distr(np.array([[2,3,8],[.1, .2, .7]]),1)[0] in [2,3,8] #doctest: \ + +ELLIPSIS +NORMALIZE_WHITESPACE + 1 + >>> np.all(np.isin(randon_int_distr(np.array([[2,3,8],[.1, .2, .7]]),5), [2,3,8])) \ + #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + True + """ + samples = [] + + cdf = np.cumsum(proba_dist[1, :]) # Cumulative Distribution Function + + for _ in range(n_sample): + proba = random() # generate a probability value uniformly between 0 and 1 + rand_value_index = np.argmax(cdf > proba) # transform generated probability to a value from the distribution using cdf function + samples.append(int(proba_dist[0, rand_value_index])) + + return samples + + +if __name__ == "__main__": + import doctest + doctest.testmod()