U_EDF.py 4.39 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 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 47 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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
"""
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


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):
        t = self.sim.now()
        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):
        delta = self.sim.now() - self.last_event

        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()

        self.last_event = self.sim.now()

        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