Skip to content
Snippets Groups Projects
Commit 87bf24d6 authored by Simon Bjurek's avatar Simon Bjurek
Browse files

redid schedule/scheduler interface to new standard

parent b8f2960e
No related branches found
No related tags found
1 merge request!461Finalize earliest deadline scheduler
......@@ -33,7 +33,7 @@ from b_asic.operation import Operation
from b_asic.port import InputPort, OutputPort
from b_asic.process import MemoryVariable, OperatorProcess
from b_asic.resources import ProcessCollection
from b_asic.scheduler import Scheduler, SchedulingAlgorithm
from b_asic.scheduler import Scheduler
from b_asic.signal_flow_graph import SFG
from b_asic.special_operations import Delay, Input, Output
from b_asic.types import TypeName
......@@ -94,12 +94,11 @@ class Schedule:
def __init__(
self,
sfg: SFG,
scheduler: Optional[Scheduler] = None,
schedule_time: Optional[int] = None,
cyclic: bool = False,
algorithm: SchedulingAlgorithm = "ASAP",
start_times: Optional[Dict[GraphID, int]] = None,
laps: Optional[Dict[GraphID, int]] = None,
max_resources: Optional[Dict[TypeName, int]] = None,
):
"""Construct a Schedule from an SFG."""
if not isinstance(sfg, SFG):
......@@ -112,14 +111,10 @@ class Schedule:
self._y_locations = defaultdict(_y_locations_default)
self._schedule_time = schedule_time
self.scheduler = Scheduler(self)
if algorithm == "ASAP":
self.scheduler.schedule_asap()
elif algorithm == "ALAP":
self.scheduler.schedule_alap()
elif algorithm == "earliest_deadline":
self.scheduler.schedule_earliest_deadline([])
elif algorithm == "provided":
if scheduler:
self._scheduler = scheduler
scheduler.apply_scheduling(self)
else:
if start_times is None:
raise ValueError("Must provide start_times when using 'provided'")
if laps is None:
......@@ -127,11 +122,8 @@ class Schedule:
self._start_times = start_times
self._laps.update(laps)
self._remove_delays_no_laps()
else:
raise NotImplementedError(f"No algorithm with name: {algorithm} defined.")
max_end_time = self.get_max_end_time()
if schedule_time is None:
self._schedule_time = max_end_time
elif schedule_time < max_end_time:
......@@ -399,6 +391,12 @@ class Schedule:
"""The start times of the operations in the schedule."""
return self._start_times
@start_times.setter
def start_times(self, start_times: dict[GraphID, int]) -> None:
if not isinstance(start_times, dict[GraphID, int]):
raise TypeError("start_times must be a dict[GraphID, int]")
self._start_times = start_times
@property
def laps(self) -> Dict[GraphID, int]:
"""
......@@ -770,7 +768,7 @@ class Schedule:
self._sfg = cast(SFG, self._sfg.remove_operation(delay_op.graph_id))
delay_list = self._sfg.find_by_type_name(Delay.type_name())
def _remove_delays(self) -> None:
def remove_delays(self) -> None:
"""Remove delay elements and update laps. Used after scheduling algorithm."""
delay_list = self._sfg.find_by_type_name(Delay.type_name())
while delay_list:
......
from enum import Enum
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, cast
from b_asic.operation import Operation
from b_asic.port import OutputPort
from b_asic.special_operations import Delay, Output
from b_asic.types import TypeName
if TYPE_CHECKING:
from b_asic.schedule import Schedule
class SchedulingAlgorithm(Enum):
ASAP = "ASAP"
ALAP = "ALAP"
EARLIEST_DEADLINE = "earliest_deadline"
# LEAST_SLACK = "least_slack" # to be implemented
PROVIDED = "provided"
# class SchedulingAlgorithm(Enum):
# ASAP = "ASAP"
# ALAP = "ALAP"
# EARLIEST_DEADLINE = "earliest_deadline"
# # LEAST_SLACK = "least_slack" # to be implemented
# PROVIDED = "provided"
class Scheduler:
def __init__(self, schedule: "Schedule") -> None:
self.schedule = schedule
class Scheduler(ABC):
@abstractmethod
def apply_scheduling(self, schedule: "Schedule") -> None:
pass
def schedule_asap(self) -> None:
"""Schedule the operations using as-soon-as-possible scheduling."""
sched = self.schedule
prec_list = sched.sfg.get_precedence_list()
def _handle_outputs(self, schedule, non_schedulable_ops) -> None:
for output in schedule.sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
source_port = cast(OutputPort, output.inputs[0].signals[0].source)
if source_port.operation.graph_id in non_schedulable_ops:
schedule.start_times[output.graph_id] = 0
else:
if source_port.latency_offset is None:
raise ValueError(
f"Output port {source_port.index} of operation"
f" {source_port.operation.graph_id} has no"
" latency-offset."
)
schedule.start_times[output.graph_id] = schedule.start_times[
source_port.operation.graph_id
] + cast(int, source_port.latency_offset)
class ASAPScheduler(Scheduler):
def apply_scheduling(self, schedule: "Schedule") -> None:
prec_list = schedule.sfg.get_precedence_list()
if len(prec_list) < 2:
raise ValueError("Empty signal flow graph cannot be scheduled.")
......@@ -34,21 +52,19 @@ class Scheduler:
operation = outport.operation
if operation.type_name() == Delay.type_name():
non_schedulable_ops.add(operation.graph_id)
# elif operation.graph_id not in sched._start_times:
else:
sched._start_times[operation.graph_id] = 0
schedule.start_times[operation.graph_id] = 0
# handle second set in precedence graph (first operations)
for outport in prec_list[1]:
operation = outport.operation
# if operation.graph_id not in sched._start_times:
sched._start_times[operation.graph_id] = 0
schedule.start_times[operation.graph_id] = 0
# handle the remaining sets
for outports in prec_list[2:]:
for outport in outports:
operation = outport.operation
if operation.graph_id not in sched._start_times:
if operation.graph_id not in schedule.start_times:
op_start_time = 0
for current_input in operation.inputs:
if len(current_input.signals) != 1:
......@@ -64,7 +80,7 @@ class Scheduler:
if source_port.operation.graph_id in non_schedulable_ops:
source_end_time = 0
else:
source_op_time = sched._start_times[
source_op_time = schedule.start_times[
source_port.operation.graph_id
]
......@@ -91,41 +107,42 @@ class Scheduler:
)
op_start_time = max(op_start_time, op_start_time_from_in)
sched._start_times[operation.graph_id] = op_start_time
schedule.start_times[operation.graph_id] = op_start_time
self._handle_outputs(schedule, non_schedulable_ops)
schedule.remove_delays()
self._handle_outputs_and_delays(non_schedulable_ops)
def schedule_alap(self) -> None:
"""Schedule the operations using as-late-as-possible scheduling."""
self.schedule_asap()
sched = self.schedule
max_end_time = sched.get_max_end_time()
class ALAPScheduler(Scheduler):
def apply_scheduling(self, schedule: "Schedule") -> None:
# self.schedule_asap()
ASAPScheduler().apply_scheduling(schedule)
max_end_time = schedule.get_max_end_time()
if sched.schedule_time is None:
sched.set_schedule_time(max_end_time)
elif sched.schedule_time < max_end_time:
if schedule.schedule_time is None:
schedule.set_schedule_time(max_end_time)
elif schedule.schedule_time < max_end_time:
raise ValueError(f"Too short schedule time. Minimum is {max_end_time}.")
# move all outputs ALAP before operations
for output in sched.sfg.find_by_type_name(Output.type_name()):
for output in schedule.sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
sched.move_operation_alap(output.graph_id)
schedule.move_operation_alap(output.graph_id)
# move all operations ALAP
for step in reversed(sched.sfg.get_precedence_list()):
for step in reversed(schedule.sfg.get_precedence_list()):
for outport in step:
if not isinstance(outport.operation, Delay):
sched.move_operation_alap(outport.operation.graph_id)
schedule.move_operation_alap(outport.operation.graph_id)
def schedule_earliest_deadline(
self, process_elements: dict[Operation, int]
) -> None:
"""Schedule the operations using earliest deadline scheduling."""
# ACT BASED ON THE NUMBER OF PEs!
class EarliestDeadlineScheduler(Scheduler):
def __init__(self, max_resources: dict[TypeName, int]) -> None:
self._max_resources = max_resources
sched = self.schedule
prec_list = sched.sfg.get_precedence_list()
def apply_scheduling(self, schedule: "Schedule") -> None:
# ACT BASED ON THE NUMBER OF PEs!
prec_list = schedule.sfg.get_precedence_list()
if len(prec_list) < 2:
raise ValueError("Empty signal flow graph cannot be scheduled.")
......@@ -135,8 +152,8 @@ class Scheduler:
operation = outport.operation
if operation.type_name() == Delay.type_name():
non_schedulable_ops.add(operation.graph_id)
elif operation.graph_id not in sched._start_times:
sched._start_times[operation.graph_id] = 0
elif operation.graph_id not in schedule.start_times:
schedule.start_times[operation.graph_id] = 0
current_time = 0
sorted_outports = sorted(
......@@ -144,50 +161,30 @@ class Scheduler:
)
for outport in sorted_outports:
op = outport.operation
sched._start_times[op.graph_id] = current_time
schedule.start_times[op.graph_id] = current_time
current_time += 1
for outports in prec_list[2:]:
# try all remaining operations for one time step
candidates = []
current_time -= 1
while len(candidates) == 0:
current_time += 1
for outport in outports:
remaining_op = outport.operation
for outport in outports:
op = outport.operation
candidates = []
while not candidates:
current_time += 1
op_is_ready_to_be_scheduled = True
for op_input in remaining_op.inputs:
for op_input in op.inputs:
source_op = op_input.signals[0].source.operation
source_op_time = sched.start_times[source_op.graph_id]
source_op_time = schedule.start_times[source_op.graph_id]
source_end_time = source_op_time + source_op.latency
if source_end_time > current_time:
op_is_ready_to_be_scheduled = False
if op_is_ready_to_be_scheduled:
candidates.append(remaining_op)
# sched._start_times[remaining_op.graph_id] = current_time
sorted_candidates = sorted(
candidates, key=lambda candidate: candidate.latency
)
# schedule the best candidate to current time
sched._start_times[sorted_candidates[0].graph_id] = current_time
self._handle_outputs_and_delays(non_schedulable_ops)
def _handle_outputs_and_delays(self, non_schedulable_ops) -> None:
sched = self.schedule
for output in sched._sfg.find_by_type_name(Output.type_name()):
output = cast(Output, output)
source_port = cast(OutputPort, output.inputs[0].signals[0].source)
if source_port.operation.graph_id in non_schedulable_ops:
sched._start_times[output.graph_id] = 0
else:
if source_port.latency_offset is None:
raise ValueError(
f"Output port {source_port.index} of operation"
f" {source_port.operation.graph_id} has no"
" latency-offset."
)
sched._start_times[output.graph_id] = sched._start_times[
source_port.operation.graph_id
] + cast(int, source_port.latency_offset)
sched._remove_delays()
candidates.append(op)
sorted_candidates = sorted(
candidates, key=lambda candidate: candidate.latency
)
# schedule the best candidate to current time
schedule.start_times[sorted_candidates[0].graph_id] = current_time
self._handle_outputs(schedule, non_schedulable_ops)
schedule.remove_delays()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment