U_EDF.py 4.46 KB
Newer Older
1 2 3 4 5 6 7 8 9
"""
Implementation of the U-EDF scheduler as presented by Nelissen et al. in
"U-EDF an unfair but optimal multiprocessor scheduling algorithm for sporadic
tasks"
"""


from simso.core import Scheduler, Timer
from math import ceil
10
from simso.schedulers import scheduler
11

12
@scheduler("simso.schedulers.U_EDF")
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
class U_EDF(Scheduler):
    def init(self):
        self.al = {}
        self.timer = None
        self.toresched = False
        self.running_jobs = {}
        self.last_event = 0
        self.newly_activated = False

        self.ui = {task: task.wcet / task.period for task in self.task_list}

    def reschedule(self):
        if not self.toresched:
            self.processors[0].resched()
        self.toresched = True

    def hp(self, job):
        for task in self.sorted_task_list:
            if task.job is job:
                return
            else:
                yield task.job

    def res(self, job, j, t1, t2):
        s = self.s[job]
        u = max(0, min(1, (s + self.ui[job.task] - j)))\
            - max(0, min(1, (s - j)))
        return (t2 - t1) * u

    def bdg(self, job, j, t2):
        return self.al[job][j] + self.res(
            job, j, job.absolute_deadline * self.sim.cycles_per_ms, t2)

    def compute_al(self):
Michael Schmid committed
47
        t = self.sim.now
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
        cycles_per_ms = self.sim.cycles_per_ms

        self.sorted_task_list = sorted(
            self.task_list,
            key=lambda t: (t.job.absolute_deadline, t.identifier))

        self.s = {}
        for task in self.task_list:
            self.al[task.job] = [0] * len(self.processors)
            self.s[task.job] = sum(self.ui[x.task] for x in self.hp(task.job))

        for task in self.sorted_task_list:
            job = task.job

            if not job.is_active():
                continue

            for j in range(len(self.processors)):
                almax = (job.absolute_deadline * cycles_per_ms - t) \
                    - sum(self.bdg(x, j, job.absolute_deadline * cycles_per_ms)
                          for x in self.hp(job)) - sum(self.al[job])
                self.al[job][j] = int(ceil(min(
                    almax, job.ret * self.sim.cycles_per_ms
                    - sum(self.al[job]))))

    def on_activate(self, job):
        self.newly_activated = True
        self.reschedule()

    def update_al(self):
Michael Schmid committed
78
        delta = self.sim.now - self.last_event
79 80 81 82 83 84 85 86 87 88 89 90 91

        for job, j in self.running_jobs.items():
            self.al[job][j] -= delta

    def schedule(self, cpu):
        self.toresched = False

        if self.newly_activated:
            self.compute_al()
            self.newly_activated = False
        else:
            self.update_al()

Michael Schmid committed
92
        self.last_event = self.sim.now
93 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

        next_event = None
        decisions = []
        selected_jobs = {}

        # Select the jobs:
        for j, proc in enumerate(self.processors):
            eligible = [task.job for task in self.task_list
                        if task.job.is_active()
                        and task.job not in selected_jobs
                        and task.job in self.al
                        and self.al[task.job][j] > 0]

            if not eligible:
                continue

            job = min(eligible,
                      key=lambda x: (x.absolute_deadline, x.task.identifier))
            if next_event is None or next_event > self.al[job][j]:
                next_event = self.al[job][j]
            selected_jobs[job] = j

        # Set the timer for the next event:
        if self.timer:
            self.timer.stop()
            self.timer = None
        if next_event is not None:
            self.timer = Timer(self.sim, U_EDF.reschedule, (self,),
                               next_event, self.processors[0], in_ms=False)
            self.timer.start()

        # Bind jobs to processors:
        jobs = list(selected_jobs.keys())
        available_procs = list(self.processors)
        was_not_running = []
        for job in jobs:
            if job in self.running_jobs:
                available_procs.remove(job.cpu)
            else:
                was_not_running.append(job)

        remaining_jobs = []
        for job in was_not_running:
            if job.cpu in available_procs:
                decisions.append((job, job.cpu))
                available_procs.remove(job.cpu)
            else:
                remaining_jobs.append(job)

        for p, job in enumerate(remaining_jobs):
            decisions.append((job, available_procs[p]))

        self.running_jobs = selected_jobs

        return decisions