From 9a73281b554fbdc166601d480970726d36be846c Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Mon, 14 Apr 2025 15:03:53 +0000 Subject: [PATCH] Fix scheduler execution-time bug, update README and split long tests --- README.md | 2 + b_asic/scheduler.py | 37 +++++------ test/integration/test_sfg_to_architecture.py | 69 ++++++++++++++++---- 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index b2b827ee..55e764be 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ The following packages are required in order to build the library: - [setuptools_scm](https://github.com/pypa/setuptools_scm/) - [NetworkX](https://networkx.org/) - [QtAwesome](https://github.com/spyder-ide/qtawesome/) + - [PuLP](https://github.com/coin-or/pulp) - Qt 6, with Python bindings, one of: (install with `pip install .[$BINDING_NAME]`) - pyqt6 - pyside6 @@ -42,6 +43,7 @@ To run the test suite, the following additional packages are required: - [pytest-cov](https://pytest-cov.readthedocs.io/en/latest/) (for testing with coverage) - [pytest-xvfb](https://github.com/The-Compiler/pytest-xvfb) (for testing without showing windows on Linux, you will also need to install [xvfb](https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml)) - [pytest-xdist](https://pytest-xdist.readthedocs.io/) (for parallel testing) + - [SciPy](https://scipy.org/) To generate the documentation, the following additional packages are required: diff --git a/b_asic/scheduler.py b/b_asic/scheduler.py index cbd5160b..17523302 100644 --- a/b_asic/scheduler.py +++ b/b_asic/scheduler.py @@ -432,17 +432,10 @@ class ListScheduler(Scheduler): if self._schedule._schedule_time is not None else 0 ) - time_slot = ( - self._current_time - if self._schedule._schedule_time is None - else self._current_time % self._schedule._schedule_time - ) ready_ops = [ op_id for op_id in candidate_ids - if self._op_is_schedulable( - self._sfg.find_by_id(op_id), schedule_time, time_slot - ) + if self._op_is_schedulable(self._sfg.find_by_id(op_id), schedule_time) ] memory_reads = self._calculate_memory_reads(ready_ops) @@ -514,14 +507,18 @@ class ListScheduler(Scheduler): count += 1 return count - def _op_satisfies_resource_constraints( - self, op: "Operation", time_slot: int - ) -> bool: + def _op_satisfies_resource_constraints(self, op: "Operation") -> bool: op_type = type(op) - return ( - self._cached_execution_times_in_time[op_type][time_slot] - < self._remaining_resources[op_type] - ) + for i in range(max(1, op.execution_time)): + time_slot = ( + self._current_time + i + if self._schedule._schedule_time is None + else (self._current_time + i) % self._schedule._schedule_time + ) + count = self._cached_execution_times_in_time[op_type][time_slot] + if count >= self._remaining_resources[op_type]: + return False + return True def _op_satisfies_concurrent_writes(self, op: "Operation") -> bool: if self._max_concurrent_writes: @@ -607,11 +604,9 @@ class ListScheduler(Scheduler): return False return True - def _op_is_schedulable( - self, op: "Operation", schedule_time: int, time_slot: int - ) -> bool: + def _op_is_schedulable(self, op: "Operation", schedule_time: int) -> bool: return ( - self._op_satisfies_resource_constraints(op, time_slot) + self._op_satisfies_resource_constraints(op) and self._op_satisfies_data_dependencies(op, schedule_time) and self._op_satisfies_concurrent_writes(op) and self._op_satisfies_concurrent_reads(op) @@ -771,7 +766,7 @@ class ListScheduler(Scheduler): time_slot = ( (self._current_time + i) % self._schedule._schedule_time if self._schedule._schedule_time - else self._current_time + else self._current_time + i ) self._cached_execution_times_in_time[type(next_op)][time_slot] += 1 @@ -1008,7 +1003,7 @@ class RecursiveListScheduler(ListScheduler): time_slot = ( (self._current_time + i) % self._schedule._schedule_time if self._schedule._schedule_time - else self._current_time + else self._current_time + i ) self._cached_execution_times_in_time[op_type][time_slot] += 1 diff --git a/test/integration/test_sfg_to_architecture.py b/test/integration/test_sfg_to_architecture.py index 11c4763a..10a5ce0d 100644 --- a/test/integration/test_sfg_to_architecture.py +++ b/test/integration/test_sfg_to_architecture.py @@ -156,7 +156,7 @@ def test_pe_and_memory_constrained_schedule(): assert arch.schedule_time == schedule.schedule_time -def test_different_resource_algorithms(): +def test_heuristic_resource_algorithms(): POINTS = 32 sfg = radix_2_dif_fft(POINTS) sfg.set_latency_of_type(Butterfly, 1) @@ -170,14 +170,14 @@ def test_different_resource_algorithms(): Input.type_name(): 1, Output.type_name(): 1, } - schedule_1 = Schedule( + schedule = Schedule( sfg, scheduler=HybridScheduler( resources, max_concurrent_reads=4, max_concurrent_writes=4 ), ) - operations = schedule_1.get_operations() + operations = schedule.get_operations() bfs = operations.get_by_type_name(Butterfly.type_name()) bfs = bfs.split_on_execution_time() const_muls = operations.get_by_type_name(ConstantMultiplication.type_name()) @@ -196,7 +196,7 @@ def test_different_resource_algorithms(): processing_elements = [bf_pe_1, bf_pe_2, mul_pe_1, mul_pe_2, pe_in, pe_out] - mem_vars = schedule_1.get_memory_variables() + mem_vars = schedule.get_memory_variables() direct, mem_vars = mem_vars.split_on_length() # LEFT-EDGE @@ -314,7 +314,8 @@ def test_different_resource_algorithms(): assert len(arch.processing_elements) == 6 assert len(arch.memories) == 7 - # FOR ILP points is reduced due to time complexity + +def test_ilp_resource_algorithms(): POINTS = 16 sfg = radix_2_dif_fft(POINTS) sfg.set_latency_of_type(Butterfly, 1) @@ -322,14 +323,20 @@ def test_different_resource_algorithms(): sfg.set_execution_time_of_type(Butterfly, 1) sfg.set_execution_time_of_type(ConstantMultiplication, 1) - schedule_2 = Schedule( + resources = { + Butterfly.type_name(): 2, + ConstantMultiplication.type_name(): 2, + Input.type_name(): 1, + Output.type_name(): 1, + } + schedule = Schedule( sfg, scheduler=HybridScheduler( resources, max_concurrent_reads=4, max_concurrent_writes=4 ), ) - operations = schedule_2.get_operations() + operations = schedule.get_operations() bfs = operations.get_by_type_name(Butterfly.type_name()) bfs = bfs.split_on_execution_time() const_muls = operations.get_by_type_name(ConstantMultiplication.type_name()) @@ -346,7 +353,7 @@ def test_different_resource_algorithms(): processing_elements = [bf_pe_1, bf_pe_2, mul_pe_1, pe_in, pe_out] - mem_vars = schedule_2.get_memory_variables() + mem_vars = schedule.get_memory_variables() direct, mem_vars = mem_vars.split_on_length() # ILP COLOR @@ -468,7 +475,45 @@ def test_different_resource_algorithms(): assert len(arch.processing_elements) == 5 assert len(arch.memories) == 4 - # ILP COLOR MIN TOTAL MUX (custom solver) + +def test_ilp_resource_algorithm_custom_solver(): + POINTS = 16 + sfg = radix_2_dif_fft(POINTS) + sfg.set_latency_of_type(Butterfly, 3) + sfg.set_latency_of_type(ConstantMultiplication, 8) + sfg.set_execution_time_of_type(Butterfly, 2) + sfg.set_execution_time_of_type(ConstantMultiplication, 8) + + resources = { + Butterfly.type_name(): 1, + ConstantMultiplication.type_name(): 1, + Input.type_name(): 1, + Output.type_name(): 1, + } + schedule = Schedule( + sfg, + scheduler=HybridScheduler( + resources, max_concurrent_reads=3, max_concurrent_writes=3 + ), + ) + + operations = schedule.get_operations() + bfs = operations.get_by_type_name(Butterfly.type_name()) + const_muls = operations.get_by_type_name(ConstantMultiplication.type_name()) + inputs = operations.get_by_type_name(Input.type_name()) + outputs = operations.get_by_type_name(Output.type_name()) + + bf_pe = ProcessingElement(bfs, entity_name="bf1") + mul_pe = ProcessingElement(const_muls, entity_name="mul1") + + pe_in = ProcessingElement(inputs, entity_name="input") + pe_out = ProcessingElement(outputs, entity_name="output") + + processing_elements = [bf_pe, mul_pe, pe_in, pe_out] + + mem_vars = schedule.get_memory_variables() + direct, mem_vars = mem_vars.split_on_length() + from pulp import PULP_CBC_CMD mem_vars_set = mem_vars.split_on_ports( @@ -477,7 +522,7 @@ def test_different_resource_algorithms(): total_ports=2, strategy="ilp_min_total_mux", processing_elements=processing_elements, - max_colors=4, + max_colors=3, solver=PULP_CBC_CMD(), ) @@ -492,5 +537,5 @@ def test_different_resource_algorithms(): memories, direct_interconnects=direct, ) - assert len(arch.processing_elements) == 5 - assert len(arch.memories) == 4 + assert len(arch.processing_elements) == 4 + assert len(arch.memories) == 3 -- GitLab