From bc0326a1bf38e5b31502c79c028ec734ccbc0923 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Wed, 29 Jan 2025 15:38:11 +0100 Subject: [PATCH] seems like its working for execution time 1 --- b_asic/scheduler.py | 122 +++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 46 deletions(-) diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index 554ce97e..a7d1b85a 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -1,8 +1,9 @@ from abc import ABC, abstractmethod +from collections import defaultdict from typing import TYPE_CHECKING, Optional, cast from b_asic.port import OutputPort -from b_asic.special_operations import Delay, Output +from b_asic.special_operations import Delay, Input, Output from b_asic.types import TypeName if TYPE_CHECKING: @@ -21,7 +22,7 @@ class Scheduler(ABC): """ pass - def _handle_outputs(self, schedule, non_schedulable_ops) -> None: + def _handle_outputs(self, schedule, non_schedulable_ops=set()) -> 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) @@ -165,8 +166,11 @@ class EarliestDeadlineScheduler(Scheduler): resource is used. """ - def __init__(self, max_resources: Optional[dict[TypeName, int]]) -> None: - self._max_resources = max_resources + def __init__(self, max_resources: Optional[dict[TypeName, int]] = None) -> None: + if max_resources: + self._max_resources = max_resources + else: + self._max_resources = {} def apply_scheduling(self, schedule: "Schedule") -> None: """Applies the scheduling algorithm on the given Schedule. @@ -176,50 +180,76 @@ class EarliestDeadlineScheduler(Scheduler): schedule : Schedule Schedule to apply the scheduling algorithm on. """ - # 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.") - # handle the first set in precedence graph (input and delays) - non_schedulable_ops = set() - for outport in prec_list[0]: - operation = outport.operation - if operation.type_name() == Delay.type_name(): - non_schedulable_ops.add(operation.graph_id) - elif operation.graph_id not in schedule.start_times: - schedule.start_times[operation.graph_id] = 0 + # TODO: Take the execution time into consideration! + ALAPScheduler().apply_scheduling(schedule) + + # move all inputs ASAP to ensure correct operation + for input_op in schedule.sfg.find_by_type_name(Input.type_name()): + input_op = cast(Input, input_op) + schedule.move_operation_asap(input_op.graph_id) + + remaining_ops = set(schedule.start_times.keys()) + remaining_ops = {elem for elem in remaining_ops if not elem.startswith("in")} + + remaining_resources = self._max_resources.copy() current_time = 0 - sorted_outports = sorted( - prec_list[1], key=lambda outport: outport.operation.latency - ) - for outport in sorted_outports: - op = outport.operation - schedule.start_times[op.graph_id] = current_time - current_time += 1 + while remaining_ops: + best_candidate = self._find_best_candidate( + schedule, remaining_ops, remaining_resources, current_time + ) + + if not best_candidate: + current_time += 1 + remaining_resources = self._max_resources.copy() + continue + + # update remaining resources + if best_candidate.type_name() in remaining_resources: + remaining_resources[best_candidate.type_name()] -= 1 + + remaining_ops.remove(best_candidate.graph_id) + schedule.start_times[best_candidate.graph_id] = current_time + + # move all inputs and outputs ALAP now that operations have moved + for input_op in schedule.sfg.find_by_type_name(Input.type_name()): + input_op = cast(Input, input_op) + schedule.move_operation_alap(input_op.graph_id) + self._handle_outputs(schedule) + + @staticmethod + def _find_best_candidate( + schedule, remaining_ops, remaining_resources, current_time + ): + # precompute source end times for faster checks + sfg = schedule.sfg + source_end_times = defaultdict(float) + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) + for op_input in operation.inputs: + source_op = op_input.signals[0].source.operation + if not isinstance(source_op, Delay): + source_end_times[op_id] = max( + source_end_times[op_id], + schedule.start_times[source_op.graph_id] + source_op.latency, + ) - for outports in prec_list[2:]: - current_time -= 1 - 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 op.inputs: - source_op = op_input.signals[0].source.operation - 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(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 + best_candidate = None + best_deadline = float('inf') + for op_id in remaining_ops: + operation = sfg.find_by_id(op_id) - self._handle_outputs(schedule, non_schedulable_ops) - schedule.remove_delays() + # check resource constraints + if operation.type_name() in remaining_resources: + if remaining_resources[operation.type_name()] == 0: + continue + + # check if all inputs are available + if source_end_times[op_id] <= current_time: + operation_deadline = schedule.start_times[op_id] + operation.latency + if operation_deadline < best_deadline: + best_candidate = operation + best_deadline = operation_deadline + + return best_candidate -- GitLab