From 3c01e0cdb34861df6670d0cbcabb0a49418cd47f Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Mon, 26 Sep 2022 15:11:22 -0500 Subject: [PATCH 01/19] Temp commit --- loopy/kernel/__init__.py | 73 +++++++++++++++++++++++++++++++++++++ loopy/kernel/instruction.py | 11 +++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index 8a8001d32..555452cc6 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -80,6 +80,13 @@ class _BoundsRecord: upper_bound_pw_aff: isl.PwAff size: isl.PwAff +@dataclass +class RelationInfo: + insn_id: str + var_name: str + relation: isl.Map + map_type: str + dep_type: str PreambleGenerator = Callable[["PreambleInfo"], Iterator[Tuple[int, str]]] @@ -640,6 +647,72 @@ def _remove_inames_for_shared_hw_axes(self, cond_inames): # {{{ dependency wrangling + def generate_access_relations(self): + from loopy.symbolic import BatchedAccessMapMapper + bmap = BatchedAccessMapMapper(self, self.all_variable_names()) + get_map = bmap.access_maps + + # generate all access relations + for insn in self.instructions: + bmap(insn.assignee, insn.within_inames) + bmap(insn.expression, insn.within_inames) + + read_rels = [RelationInfo(insn.id, var, get_map[var][insn.within_inames], + "read") + for insn in self.instructions + for var in insn.read_dependency_names() - insn.within_inames] + write_rels = [RelationInfo(insn.id, var, get_map[var][insn.within_inames], + "write") + for insn in self.instructions + for var in insn.write_dependency_names() - insn.within_inames] + + return read_rels, write_rels + + def generate_dep_relations(self, read_rels, write_rels): + + # read-write `insn.depends_on` updates + for r_rel in read_rels: + for w_rel in write_rels: + if r_rel.var_name == w_rel.var_name: + rw_rel = r_rel.relation.apply_range(w_rel.relation.reverse()) + rw_dep = RelationInfo(r_rel.id, r_rel.var_name, rw_rel, "dep", + "rw") + for insn in self.instructions: + if insn.id == r_rel.insn_id: + insn.depends_on(rw_dep) + + # write-read `insn.depends_on` updates + for w_rel in write_rels: + for r_rel in read_rels: + if w_rel.var_name == r_rel.var_name: + wr_rel = w_rel.relation.apply_range(r_rel.relation.reverse()) + wr_dep = RelationInfo(w_rel.id, w_rel.var_name, rw_rel, "dep", + "wr") + for insn in self.instructions: + if insn.id == w_rel.insn_id: + insn.depends_on(wr_dep) + + # write-write `insn.depends_on` updates + for w1_rel in write_rels: + for w2_rel in write_rels: + if w1_rel.var_name == w2_rel.var_name: + ww_rel = w1_rel.relation.apply_range(w2_rel.relation.reverse()) + ww_dep = RelationInfo(w1_rel.id, w1_rel.var_name, ww_dep, + "dep", "ww") + for insn in self.instructions: + if insn.id == w1_rel.insn_id: + insn.depends_on(ww_dep) + + + def dep_finder(self): + + # generate access relations for each statement + read_rels, write_rels = generate_access_relations() + + # generate dependency relations and store results in each statement + generate_dep_relations(read_rels, write_rels) + + @memoize_method def recursive_insn_dep_map(self): """Returns a :class:`dict` mapping an instruction IDs *a* diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 1f04b549f..dfbe62e42 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -315,6 +315,16 @@ def __init__(self, id, depends_on, depends_on_is_final, # {{{ abstract interface + @property + def depends_on(self, dep_relation): + if self.depends_on is None: + self.depends_on = frozenset() + + result = self.depends_on | frozenset({dep_relation}) + + return result + + def read_dependency_names(self): from loopy.symbolic import get_dependencies result = frozenset() @@ -471,7 +481,6 @@ def __setstate__(self, val): # }}} - def _get_assignee_var_name(expr): from pymbolic.primitives import Variable, Subscript, Lookup from loopy.symbolic import LinearSubscript, SubArrayRef From a5ee5829d06104b18c73dc8f288c2d3200384f28 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 1 Oct 2022 21:04:33 -0500 Subject: [PATCH 02/19] Updated kernel-level dependency finding mechanisms. - Changed function that generates access relations. (`generate_access_relations()`) - Changed function that generates dependency relations. (`generate_dependency_relations()`) - Added data classes that store information relevant to. - (`RelationInfo`, `AccessRelation(RelationInfo)`, `DependencyRelation(RelationInfo)` --- loopy/kernel/__init__.py | 109 +++++++++++++++++------------------- loopy/kernel/instruction.py | 10 ---- 2 files changed, 52 insertions(+), 67 deletions(-) diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index 555452cc6..90625e9bb 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -80,13 +80,20 @@ class _BoundsRecord: upper_bound_pw_aff: isl.PwAff size: isl.PwAff -@dataclass +@dataclass(frozen=True) class RelationInfo: insn_id: str var_name: str relation: isl.Map - map_type: str - dep_type: str + +@dataclass(frozen=True) +class AccessRelation(RelationInfo): + access_type: str + +@dataclass(frozen=True) +class DependencyRelation(RelationInfo): + dependent_id: str + dependency_type: str PreambleGenerator = Callable[["PreambleInfo"], Iterator[Tuple[int, str]]] @@ -648,70 +655,58 @@ def _remove_inames_for_shared_hw_axes(self, cond_inames): # {{{ dependency wrangling def generate_access_relations(self): - from loopy.symbolic import BatchedAccessMapMapper + from loopy.symbolic import BatchedAccessMapMapper bmap = BatchedAccessMapMapper(self, self.all_variable_names()) - get_map = bmap.access_maps - - # generate all access relations for insn in self.instructions: bmap(insn.assignee, insn.within_inames) bmap(insn.expression, insn.within_inames) - read_rels = [RelationInfo(insn.id, var, get_map[var][insn.within_inames], - "read") - for insn in self.instructions - for var in insn.read_dependency_names() - insn.within_inames] - write_rels = [RelationInfo(insn.id, var, get_map[var][insn.within_inames], - "write") - for insn in self.instructions - for var in insn.write_dependency_names() - insn.within_inames] + def get_map(var, insn): + return bmap.access_maps[var][insn.within_inames] - return read_rels, write_rels + def var_list(insn): + return insn.read_dependency_names() - insn.within_inames - def generate_dep_relations(self, read_rels, write_rels): - - # read-write `insn.depends_on` updates - for r_rel in read_rels: - for w_rel in write_rels: - if r_rel.var_name == w_rel.var_name: - rw_rel = r_rel.relation.apply_range(w_rel.relation.reverse()) - rw_dep = RelationInfo(r_rel.id, r_rel.var_name, rw_rel, "dep", - "rw") - for insn in self.instructions: - if insn.id == r_rel.insn_id: - insn.depends_on(rw_dep) - - # write-read `insn.depends_on` updates - for w_rel in write_rels: - for r_rel in read_rels: - if w_rel.var_name == r_rel.var_name: - wr_rel = w_rel.relation.apply_range(r_rel.relation.reverse()) - wr_dep = RelationInfo(w_rel.id, w_rel.var_name, rw_rel, "dep", - "wr") - for insn in self.instructions: - if insn.id == w_rel.insn_id: - insn.depends_on(wr_dep) - - # write-write `insn.depends_on` updates - for w1_rel in write_rels: - for w2_rel in write_rels: - if w1_rel.var_name == w2_rel.var_name: - ww_rel = w1_rel.relation.apply_range(w2_rel.relation.reverse()) - ww_dep = RelationInfo(w1_rel.id, w1_rel.var_name, ww_dep, - "dep", "ww") - for insn in self.instructions: - if insn.id == w1_rel.insn_id: - insn.depends_on(ww_dep) - - - def dep_finder(self): + read_maps = [AccessRelation(insn.id, var, get_map(var, insn), "read") + for insn in self.instructions + for var in var_list(insn)] + write_maps = [AccessRelation(insn.id, var, get_map(var, insn), "write") + for insn in self.instructions + for var in var_list(insn)] - # generate access relations for each statement - read_rels, write_rels = generate_access_relations() + return read_maps, write_maps - # generate dependency relations and store results in each statement - generate_dep_relations(read_rels, write_rels) + def generate_dependency_relations(self, read_maps, write_maps): + + def get_dependency_relation(X, Y): + return X.relation.apply_range(Y.relation.reverse()) + + write_read = [DependencyRelation(write.insn_id, write.var_name, + get_dependency_relation(write, read), + read.insn_id, "write-read") + for write in write_maps + for read in read_maps + if write.var_name == read.var_name] + read_write = [DependencyRelation(read.insn_id, read.var_name, + get_dependency_relation(read, write), + write.insn_id, "read-write") + for read in read_maps + for write in write_maps + if read.var_name == write.var_name] + write_write = [DependencyRelation(write1.insn_id, write1.var_name, + get_dependency_relation(write1, write2), + write2.insn_id, "write-write") + for write1 in write_maps + for write2 in write_maps + if write1.var_name == write2.var_name] + + return write_read, read_write, write_write + + def update_happens_before(self, write_read, read_write, write_write): + pass + def dep_finder(self): + pass @memoize_method def recursive_insn_dep_map(self): diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index dfbe62e42..56298c1fa 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -315,16 +315,6 @@ def __init__(self, id, depends_on, depends_on_is_final, # {{{ abstract interface - @property - def depends_on(self, dep_relation): - if self.depends_on is None: - self.depends_on = frozenset() - - result = self.depends_on | frozenset({dep_relation}) - - return result - - def read_dependency_names(self): from loopy.symbolic import get_dependencies result = frozenset() From e553cceb0341d2f5d1730b4a63b7f4b2edf4a1d3 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sun, 2 Oct 2022 20:31:35 -0500 Subject: [PATCH 03/19] Kernel now updates `depends_on` in line with the description of `depends_on`. --- loopy/kernel/__init__.py | 37 +++++++++++++++++++++++++++++-------- loopy/kernel/instruction.py | 4 ++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index 90625e9bb..63c14e931 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -664,22 +664,32 @@ def generate_access_relations(self): def get_map(var, insn): return bmap.access_maps[var][insn.within_inames] - def var_list(insn): + def read_var_list(insn): return insn.read_dependency_names() - insn.within_inames + def write_var_list(insn): + return insn.write_dependency_names() - insn.within_inames + read_maps = [AccessRelation(insn.id, var, get_map(var, insn), "read") for insn in self.instructions - for var in var_list(insn)] + for var in read_var_list(insn)] write_maps = [AccessRelation(insn.id, var, get_map(var, insn), "write") for insn in self.instructions - for var in var_list(insn)] + for var in write_var_list(insn)] return read_maps, write_maps def generate_dependency_relations(self, read_maps, write_maps): - def get_dependency_relation(X, Y): - return X.relation.apply_range(Y.relation.reverse()) + def get_dependency_relation(x, y): + + # TODO deal with self dependencies + #self_dependency = x.relation.apply_range(x.relation.reverse()) + + dependency_relation = x.relation.apply_range(y.relation.reverse()) + #dependency_relation -= self_dependency + + return dependency_relation write_read = [DependencyRelation(write.insn_id, write.var_name, get_dependency_relation(write, read), @@ -687,12 +697,14 @@ def get_dependency_relation(X, Y): for write in write_maps for read in read_maps if write.var_name == read.var_name] + read_write = [DependencyRelation(read.insn_id, read.var_name, get_dependency_relation(read, write), write.insn_id, "read-write") for read in read_maps for write in write_maps if read.var_name == write.var_name] + write_write = [DependencyRelation(write1.insn_id, write1.var_name, get_dependency_relation(write1, write2), write2.insn_id, "write-write") @@ -700,10 +712,19 @@ def get_dependency_relation(X, Y): for write2 in write_maps if write1.var_name == write2.var_name] - return write_read, read_write, write_write + # update depends_on for each instruction + for insn in self.instructions: + for relation in write_read: + if relation.dependent_id == insn.id: + insn.update_depends_on(relation.insn_id) + for relation in read_write: + if relation.dependent_id == insn.id: + insn.update_depends_on(relation.insn_id) + for relation in write_write: + if relation.dependent_id == insn.id: + insn.update_depends_on(relation.insn_id) - def update_happens_before(self, write_read, read_write, write_write): - pass + return write_read, read_write, write_write def dep_finder(self): pass diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 56298c1fa..65dbda8b3 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -315,6 +315,10 @@ def __init__(self, id, depends_on, depends_on_is_final, # {{{ abstract interface + def update_depends_on(self, dependee): + update = frozenset({dependee}) + self.depends_on = self.depends_on | update + def read_dependency_names(self): from loopy.symbolic import get_dependencies result = frozenset() From a10d3db6444e05752e07f7d296ed1139d7e067fa Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sun, 9 Oct 2022 22:51:31 -0500 Subject: [PATCH 04/19] New file created for dependencies, added type annotations, documentation, and rewrote classes storing information for relations. --- loopy/kernel/__init__.py | 75 ------------------- loopy/kernel/dependency.py | 139 ++++++++++++++++++++++++++++++++++++ loopy/kernel/instruction.py | 4 -- 3 files changed, 139 insertions(+), 79 deletions(-) create mode 100644 loopy/kernel/dependency.py diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index 63c14e931..cc63df4bb 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -654,81 +654,6 @@ def _remove_inames_for_shared_hw_axes(self, cond_inames): # {{{ dependency wrangling - def generate_access_relations(self): - from loopy.symbolic import BatchedAccessMapMapper - bmap = BatchedAccessMapMapper(self, self.all_variable_names()) - for insn in self.instructions: - bmap(insn.assignee, insn.within_inames) - bmap(insn.expression, insn.within_inames) - - def get_map(var, insn): - return bmap.access_maps[var][insn.within_inames] - - def read_var_list(insn): - return insn.read_dependency_names() - insn.within_inames - - def write_var_list(insn): - return insn.write_dependency_names() - insn.within_inames - - read_maps = [AccessRelation(insn.id, var, get_map(var, insn), "read") - for insn in self.instructions - for var in read_var_list(insn)] - write_maps = [AccessRelation(insn.id, var, get_map(var, insn), "write") - for insn in self.instructions - for var in write_var_list(insn)] - - return read_maps, write_maps - - def generate_dependency_relations(self, read_maps, write_maps): - - def get_dependency_relation(x, y): - - # TODO deal with self dependencies - #self_dependency = x.relation.apply_range(x.relation.reverse()) - - dependency_relation = x.relation.apply_range(y.relation.reverse()) - #dependency_relation -= self_dependency - - return dependency_relation - - write_read = [DependencyRelation(write.insn_id, write.var_name, - get_dependency_relation(write, read), - read.insn_id, "write-read") - for write in write_maps - for read in read_maps - if write.var_name == read.var_name] - - read_write = [DependencyRelation(read.insn_id, read.var_name, - get_dependency_relation(read, write), - write.insn_id, "read-write") - for read in read_maps - for write in write_maps - if read.var_name == write.var_name] - - write_write = [DependencyRelation(write1.insn_id, write1.var_name, - get_dependency_relation(write1, write2), - write2.insn_id, "write-write") - for write1 in write_maps - for write2 in write_maps - if write1.var_name == write2.var_name] - - # update depends_on for each instruction - for insn in self.instructions: - for relation in write_read: - if relation.dependent_id == insn.id: - insn.update_depends_on(relation.insn_id) - for relation in read_write: - if relation.dependent_id == insn.id: - insn.update_depends_on(relation.insn_id) - for relation in write_write: - if relation.dependent_id == insn.id: - insn.update_depends_on(relation.insn_id) - - return write_read, read_write, write_write - - def dep_finder(self): - pass - @memoize_method def recursive_insn_dep_map(self): """Returns a :class:`dict` mapping an instruction IDs *a* diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py new file mode 100644 index 000000000..07949fe30 --- /dev/null +++ b/loopy/kernel/dependency.py @@ -0,0 +1,139 @@ +import islpy as isl + +from loopy.symbolic import BatchedAccessMapMapper +from loopy import LoopKernel +from loopy import InstructionBase + +from dataclasses import dataclass +from enum import Enum + +class DependencyType(Enum): + """An enumeration of the types of data dependencies found in a program. + """ + WRITE_READ = 0 + READ_WRITE = 1 + WRITE_WRITE = 2 + +class AccessType(Enum): + """An enumeration of the types of accesses made by statements in a program. + """ + READ = 0 + WRITE = 1 + +@dataclass(frozen=True) +class HappensBefore: + """A class representing a "happens-before" relationship between two + statements found in a :class:`loopy.LoopKernel`. Used to validate that a + given kernel transformation respects the data dependencies in a given + program. + + .. attribute:: happens_before + The :attr:`id` of a :class:`loopy.InstructionBase` that depends on the + current :class:`loopy.InstructionBase` instance. + + .. attribute:: variable_name + The name of the variable in a program that is causing the dependency. + + .. attribute:: relation + An :class:`isl.Map` representing the data dependency. The input of the + map is an iname tuple and the output of the map is a set of iname tuples + that must execute after the input. + + .. attribute:: dependency_type + A :class:`DependencyType` of :class:`Enum` representing the dependency + type (write-read, read-write, write-write). + """ + + happens_before: str + variable_name: str + relation: isl.Map + dependency_type: DependencyType + +@dataclass(frozen=True) +class AccessRelation: + """A class that stores information about a particular array access in a + program. + .. attribute:: id + The instruction id of the statement the access relation is representing. + + .. attribute:: variable_name + The memory location the access relation is representing. + + .. attribute:: relation + An :class:`isl.Map` object representing the memory access. The access + relation is a map from the loop domain to the set of valid array + indices. + + .. attribute:: access_type + An :class:`Enum` object representing the type of memory access the + statement is making. The type of memory access is either a read or a + write. + """ + + id: str + variable_name: str + relation: isl.Map + access_type: AccessType + +def generate_dependency_relations(knl: LoopKernel) -> None: + + bmap: BatchedAccessMapMapper = BatchedAccessMapMapper(knl, + knl.all_variable_names()) + for insn in knl.instructions: + bmap(insn.assignee, insn.within_inames) + bmap(insn.expression, insn.within_inames) + + def get_map(var: str, insn: InstructionBase) -> isl.Map: + return bmap.access_maps[var][insn.within_inames] + + def read_var_list(insn: InstructionBase) -> frozenset[str]: + return insn.read_dependency_names() - insn.within_inames + + def write_var_list(insn: InstructionBase) -> frozenset[str]: + return insn.write_dependency_names() - insn.within_inames + + def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: + diagonal: isl.Map = isl.Map("{ [i,j] -> [i',j']: i = i' and j = j' }") + dependency_relation: isl.Map = x.apply_range(y.reverse()) + dependency_relation -= diagonal + + return dependency_relation + + read_maps: list[AccessRelation] = [AccessRelation(insn.id, var, + get_map(var, insn), + AccessType.READ) + for insn in knl.instructions + for var in read_var_list(insn)] + write_maps: list[AccessRelation] = [AccessRelation(insn.id, var, + get_map(var, insn), + AccessType.WRITE) + for insn in knl.instructions + for var in write_var_list(insn)] + + write_read: list[HappensBefore] = [HappensBefore(read.id, + write.variable_name, + get_dependency_relation(write.relation, + read.relation), + DependencyType.WRITE_READ) + for write in write_maps + for read in read_maps + if write.variable_name == read.variable_name] + read_write: list[HappensBefore] = [HappensBefore(write.id, + read.variable_name, + get_dependency_relation(read.relation, + write.relation), + DependencyType.READ_WRITE) + for read in read_maps + for write in write_maps + if read.variable_name == write.variable_name] + write_write: list[HappensBefore] = [HappensBefore(write2.id, + write1.variable_name, + get_dependency_relation(write1.relation, + write2.relation), + DependencyType.WRITE_WRITE) + for write1 in write_maps + for write2 in write_maps + if write1.variable_name == write2.variable_name] + + + diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 65dbda8b3..56298c1fa 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -315,10 +315,6 @@ def __init__(self, id, depends_on, depends_on_is_final, # {{{ abstract interface - def update_depends_on(self, dependee): - update = frozenset({dependee}) - self.depends_on = self.depends_on | update - def read_dependency_names(self): from loopy.symbolic import get_dependencies result = frozenset() From 81b26a00975b2667b3dff9742d9cabc671daef0c Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sun, 9 Oct 2022 23:45:08 -0500 Subject: [PATCH 05/19] Adjusted alignment so lines of code were not so far past 80 columns --- loopy/kernel/dependency.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 07949fe30..356275de7 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -75,7 +75,8 @@ class AccessRelation: relation: isl.Map access_type: AccessType -def generate_dependency_relations(knl: LoopKernel) -> None: +def generate_dependency_relations(knl: LoopKernel) \ + -> tuple[list[HappensBefore], list[HappensBefore], list[HappensBefore]]: bmap: BatchedAccessMapMapper = BatchedAccessMapMapper(knl, knl.all_variable_names()) @@ -111,29 +112,28 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: for var in write_var_list(insn)] write_read: list[HappensBefore] = [HappensBefore(read.id, - write.variable_name, - get_dependency_relation(write.relation, - read.relation), - DependencyType.WRITE_READ) + write.variable_name, + get_dependency_relation(write.relation, + read.relation), + DependencyType.WRITE_READ) for write in write_maps for read in read_maps if write.variable_name == read.variable_name] read_write: list[HappensBefore] = [HappensBefore(write.id, - read.variable_name, - get_dependency_relation(read.relation, - write.relation), - DependencyType.READ_WRITE) + read.variable_name, + get_dependency_relation(read.relation, + write.relation), + DependencyType.READ_WRITE) for read in read_maps for write in write_maps if read.variable_name == write.variable_name] write_write: list[HappensBefore] = [HappensBefore(write2.id, - write1.variable_name, - get_dependency_relation(write1.relation, - write2.relation), - DependencyType.WRITE_WRITE) + write1.variable_name, + get_dependency_relation(write1.relation, + write2.relation), + DependencyType.WRITE_WRITE) for write1 in write_maps for write2 in write_maps if write1.variable_name == write2.variable_name] - - + return write_read, read_write, write_write From 09899bfb5f9817139b87d00fe5131eeba5379723 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Mon, 10 Oct 2022 00:17:18 -0500 Subject: [PATCH 06/19] Stub in dep_finder function --- loopy/kernel/dependency.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 356275de7..490fc052d 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -137,3 +137,6 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: if write1.variable_name == write2.variable_name] return write_read, read_write, write_write + +def dependency_finder(knl: LoopKernel) -> None: + pass From 464895f82a42e9c25e63b1f127f0c2c5d5177634 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 15 Oct 2022 16:24:18 -0500 Subject: [PATCH 07/19] Add documentation, begin fleshing out --- loopy/kernel/dependency.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 490fc052d..50a2d6ada 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -77,7 +77,17 @@ class AccessRelation: def generate_dependency_relations(knl: LoopKernel) \ -> tuple[list[HappensBefore], list[HappensBefore], list[HappensBefore]]: + """Generates :class:`isl.Map` objects representing the data dependencies between + statements in a loopy program. The :class:`isl.Map` objects are stored in a + :class:`loopy.Dependency.HappensBefore` object along with the dependee id, + variable name, and dependency type. + + :arg knl: A :class:`loopy.LoopKernel` containing the instructions we wish to + find data dependencies between. + :returns: Three lists containing :class:`loopy.Dependency.HappensBefore` + objects describing the data dependencies. + """ bmap: BatchedAccessMapMapper = BatchedAccessMapMapper(knl, knl.all_variable_names()) for insn in knl.instructions: @@ -138,5 +148,17 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: return write_read, read_write, write_write -def dependency_finder(knl: LoopKernel) -> None: +def generate_execution_order(knl: LoopKernel) -> None: + """Generate the "happens-before" execution order that *must* be respected by + any transformation. Calls :function:`generate_dependency_relations` to get + the information needed to compute the execution order. + + :arg knl: A :class:`loopy.LoopKernel` containing the instructions for which + to generate a "happens-before" execution order. + """ + + write_read, read_write, write_write = generate_dependency_relations(knl) + + + pass From d947d6bacff2a8a291cf4d75b0fb97a31a0d0679 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 15 Oct 2022 16:24:18 -0500 Subject: [PATCH 08/19] Add documentation, begin fleshing out generate_execution_order --- loopy/kernel/dependency.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 50a2d6ada..d7fdd567c 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -158,7 +158,43 @@ def generate_execution_order(knl: LoopKernel) -> None: """ write_read, read_write, write_write = generate_dependency_relations(knl) - + execution_order: frozenset[isl.Set] = frozenset() + for insn in knl.instructions: + domain: isl.BasicSet = knl.get_inames_domain(insn.within_inames) + insn_order: isl.Map = domain.lex_lt_set(domain) + + # v FIXME: there must be a better way + union_of_dependencies = None + for rel in write_read: + if rel.happens_before == insn.id: + if union_of_dependencies == None: + union_of_dependencies = rel.relation + else: + union_of_dependencies = union_of_dependencies | rel.relation + for rel in read_write: + if rel.happens_before == insn.id: + if union_of_dependencies == None: + union_of_dependencies = rel.relation + else: + union_of_dependencies = union_of_dependencies | rel.relation + for rel in write_write: + if rel.happens_before == insn.id: + if union_of_dependencies == None: + union_of_dependencies = rel.relation + else: + union_of_dependencies = union_of_dependencies | rel.relation + + insn_order = insn_order & union_of_dependencies + + execution_order = execution_order | frozenset(insn_order) + +def verify_execution_order(knl: LoopKernel): + """Verify that a given transformation respects the dependencies in a + :class:`loopy.LoopKernel` program. Calls + :function:`generate_execution_order` to generate the "happens-before" for + each iname domain that *must* be respected in order for a transformation to + be valid. + """ pass From fdb5a017bd66fb3d3bdcbb76297928a398747775 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 15 Oct 2022 18:04:49 -0500 Subject: [PATCH 09/19] Implemented draft version of generate_execution_order and stub in verify_execution_order --- loopy/kernel/dependency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index d7fdd567c..b70eb3d62 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -190,7 +190,7 @@ def generate_execution_order(knl: LoopKernel) -> None: execution_order = execution_order | frozenset(insn_order) -def verify_execution_order(knl: LoopKernel): +def verify_execution_order(knl: LoopKernel, existing_happens_before: isl.Map): """Verify that a given transformation respects the dependencies in a :class:`loopy.LoopKernel` program. Calls :function:`generate_execution_order` to generate the "happens-before" for From 29ae7c00359fea0ac53e77b2a3d1d792f64e13b1 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 15 Oct 2022 18:07:34 -0500 Subject: [PATCH 10/19] Last commit does not reflect all changes made in that commit --- loopy/kernel/dependency.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index b70eb3d62..21aa9ed22 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -196,5 +196,8 @@ def verify_execution_order(knl: LoopKernel, existing_happens_before: isl.Map): :function:`generate_execution_order` to generate the "happens-before" for each iname domain that *must* be respected in order for a transformation to be valid. + + :returns: True or false depending on whether the provided execution order + respects the dependencies in the loopy program. """ pass From 029476180d3da4398675f7d5bbfbb3946aa2a7f3 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 15 Oct 2022 21:15:00 -0500 Subject: [PATCH 11/19] Rewrote execution order generation function --- loopy/kernel/dependency.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 21aa9ed22..821214fca 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -148,7 +148,7 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: return write_read, read_write, write_write -def generate_execution_order(knl: LoopKernel) -> None: +def generate_execution_order(knl: LoopKernel): """Generate the "happens-before" execution order that *must* be respected by any transformation. Calls :function:`generate_dependency_relations` to get the information needed to compute the execution order. @@ -168,27 +168,27 @@ def generate_execution_order(knl: LoopKernel) -> None: # v FIXME: there must be a better way union_of_dependencies = None for rel in write_read: - if rel.happens_before == insn.id: - if union_of_dependencies == None: - union_of_dependencies = rel.relation - else: - union_of_dependencies = union_of_dependencies | rel.relation + if union_of_dependencies is None: + union_of_dependencies = rel.relation + else: + union_of_dependencies = union_of_dependencies | rel.relation + for rel in read_write: - if rel.happens_before == insn.id: - if union_of_dependencies == None: - union_of_dependencies = rel.relation - else: - union_of_dependencies = union_of_dependencies | rel.relation + if union_of_dependencies is None: + union_of_dependencies = rel.relation + else: + union_of_dependencies = union_of_dependencies | rel.relation + for rel in write_write: - if rel.happens_before == insn.id: - if union_of_dependencies == None: - union_of_dependencies = rel.relation - else: - union_of_dependencies = union_of_dependencies | rel.relation + if union_of_dependencies is None: + union_of_dependencies = rel.relation + else: + union_of_dependencies = union_of_dependencies | rel.relation insn_order = insn_order & union_of_dependencies + execution_order = execution_order | frozenset({insn_order}) - execution_order = execution_order | frozenset(insn_order) + return execution_order def verify_execution_order(knl: LoopKernel, existing_happens_before: isl.Map): """Verify that a given transformation respects the dependencies in a From 6ed1cce5a882e06268445b1a560fad296891a13e Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 15 Oct 2022 22:32:59 -0500 Subject: [PATCH 12/19] Minor changes to draft of generate_execution_order and to the documentation of the function --- loopy/kernel/dependency.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 821214fca..e79623173 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -104,9 +104,11 @@ def write_var_list(insn: InstructionBase) -> frozenset[str]: return insn.write_dependency_names() - insn.within_inames def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: - diagonal: isl.Map = isl.Map("{ [i,j] -> [i',j']: i = i' and j = j' }") + # FIXME: how to generate a 'diagonal' map using what we know about the + # space of our domain? +# diagonal: isl.Map = isl.Map("{ [i,j] -> [i',j']: i = i' and j = j' }") dependency_relation: isl.Map = x.apply_range(y.reverse()) - dependency_relation -= diagonal +# dependency_relation -= diagonal return dependency_relation @@ -148,18 +150,21 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: return write_read, read_write, write_write -def generate_execution_order(knl: LoopKernel): +def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: """Generate the "happens-before" execution order that *must* be respected by any transformation. Calls :function:`generate_dependency_relations` to get the information needed to compute the execution order. :arg knl: A :class:`loopy.LoopKernel` containing the instructions for which to generate a "happens-before" execution order. + + :returns: A :class:`frozenset` of :class:`isl.Map` representing the + execution required by the dependencies in a loopy program. """ write_read, read_write, write_write = generate_dependency_relations(knl) - execution_order: frozenset[isl.Set] = frozenset() + execution_order: frozenset[isl.Map] = frozenset() for insn in knl.instructions: domain: isl.BasicSet = knl.get_inames_domain(insn.within_inames) From ccfca54996b5b1a8f56386006c80b17cb1f0d9d1 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Mon, 17 Oct 2022 16:42:39 -0500 Subject: [PATCH 13/19] Removed some things from kernel __init__.py that shouldn't be there --- loopy/kernel/__init__.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index cc63df4bb..7ac8b2777 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -80,24 +80,8 @@ class _BoundsRecord: upper_bound_pw_aff: isl.PwAff size: isl.PwAff -@dataclass(frozen=True) -class RelationInfo: - insn_id: str - var_name: str - relation: isl.Map - -@dataclass(frozen=True) -class AccessRelation(RelationInfo): - access_type: str - -@dataclass(frozen=True) -class DependencyRelation(RelationInfo): - dependent_id: str - dependency_type: str - PreambleGenerator = Callable[["PreambleInfo"], Iterator[Tuple[int, str]]] - @dataclass(frozen=True) class LoopKernel(Taggable): """These correspond more or less directly to arguments of From 6e47dd0651770372f7df6730c1f17a9203d264d9 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Fri, 4 Nov 2022 16:26:53 -0500 Subject: [PATCH 14/19] Updated subtraction of diagonal from found dependency relation. --- loopy/kernel/dependency.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index e79623173..b15bb5e84 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -10,9 +10,9 @@ class DependencyType(Enum): """An enumeration of the types of data dependencies found in a program. """ - WRITE_READ = 0 - READ_WRITE = 1 - WRITE_WRITE = 2 + WRITE_AFTER_READ = 0 + READ_AFTER_WRITE = 1 + WRITE_AFTER_WRITE = 2 class AccessType(Enum): """An enumeration of the types of accesses made by statements in a program. @@ -45,12 +45,12 @@ class HappensBefore: """ happens_before: str - variable_name: str + variable_name: Optional[str] relation: isl.Map - dependency_type: DependencyType + dependency_type: Optional[DependencyType] @dataclass(frozen=True) -class AccessRelation: +class _AccessRelation: """A class that stores information about a particular array access in a program. .. attribute:: id @@ -104,20 +104,18 @@ def write_var_list(insn: InstructionBase) -> frozenset[str]: return insn.write_dependency_names() - insn.within_inames def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: - # FIXME: how to generate a 'diagonal' map using what we know about the - # space of our domain? -# diagonal: isl.Map = isl.Map("{ [i,j] -> [i',j']: i = i' and j = j' }") - dependency_relation: isl.Map = x.apply_range(y.reverse()) -# dependency_relation -= diagonal + dependency: isl.Map = x.apply_range(y.reverse()) + diagonal: isl.Map = dependency.identity(dependency.get_space()) + dependency -= diagonal - return dependency_relation + return dependency - read_maps: list[AccessRelation] = [AccessRelation(insn.id, var, + read_maps: list[_AccessRelation] = [_AccessRelation(insn.id, var, get_map(var, insn), AccessType.READ) for insn in knl.instructions for var in read_var_list(insn)] - write_maps: list[AccessRelation] = [AccessRelation(insn.id, var, + write_maps: list[_AccessRelation] = [_AccessRelation(insn.id, var, get_map(var, insn), AccessType.WRITE) for insn in knl.instructions @@ -127,7 +125,7 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: write.variable_name, get_dependency_relation(write.relation, read.relation), - DependencyType.WRITE_READ) + DependencyType.WRITE_AFTER_READ) for write in write_maps for read in read_maps if write.variable_name == read.variable_name] @@ -135,7 +133,7 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: read.variable_name, get_dependency_relation(read.relation, write.relation), - DependencyType.READ_WRITE) + DependencyType.READ_AFTER_WRITE) for read in read_maps for write in write_maps if read.variable_name == write.variable_name] @@ -143,7 +141,7 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: write1.variable_name, get_dependency_relation(write1.relation, write2.relation), - DependencyType.WRITE_WRITE) + DependencyType.WRITE_AFTER_WRITE) for write1 in write_maps for write2 in write_maps if write1.variable_name == write2.variable_name] From a0fd27443006dc972053cd09fd09d16202b5bba5 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Fri, 4 Nov 2022 19:47:54 -0500 Subject: [PATCH 15/19] Replaced dependency union operation with a reduction. Can this be reduced more? Yes (coming soon). --- loopy/kernel/dependency.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index b15bb5e84..0a3fb22dc 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -4,6 +4,8 @@ from loopy import LoopKernel from loopy import InstructionBase +from functools import reduce +from typing import Optional from dataclasses import dataclass from enum import Enum @@ -168,30 +170,18 @@ def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: domain: isl.BasicSet = knl.get_inames_domain(insn.within_inames) insn_order: isl.Map = domain.lex_lt_set(domain) - # v FIXME: there must be a better way - union_of_dependencies = None - for rel in write_read: - if union_of_dependencies is None: - union_of_dependencies = rel.relation - else: - union_of_dependencies = union_of_dependencies | rel.relation - - for rel in read_write: - if union_of_dependencies is None: - union_of_dependencies = rel.relation - else: - union_of_dependencies = union_of_dependencies | rel.relation + union_of_dependencies: isl.Map = reduce(lambda x, + y: x.relation | y.relation, + write_read) + union_of_dependencies |= reduce(lambda x, y: x.relation | y.relation, + read_write) + union_of_dependencies |= reduce(lambda x, y: x.relation | y.relation, + write_write) - for rel in write_write: - if union_of_dependencies is None: - union_of_dependencies = rel.relation - else: - union_of_dependencies = union_of_dependencies | rel.relation - - insn_order = insn_order & union_of_dependencies + insn_order: isl.Map = insn_order & union_of_dependencies execution_order = execution_order | frozenset({insn_order}) - return execution_order + return execution_order def verify_execution_order(knl: LoopKernel, existing_happens_before: isl.Map): """Verify that a given transformation respects the dependencies in a From 25690063db441e969ec3ce6df89edd5e9bccb580 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 5 Nov 2022 11:22:11 -0500 Subject: [PATCH 16/19] Added various TODOs to mark changes --- loopy/kernel/dependency.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 0a3fb22dc..07d44a6cd 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -9,6 +9,7 @@ from dataclasses import dataclass from enum import Enum +# TODO this is going away class DependencyType(Enum): """An enumeration of the types of data dependencies found in a program. """ @@ -22,6 +23,7 @@ class AccessType(Enum): READ = 0 WRITE = 1 +# TODO this might be going away @dataclass(frozen=True) class HappensBefore: """A class representing a "happens-before" relationship between two @@ -51,6 +53,7 @@ class HappensBefore: relation: isl.Map dependency_type: Optional[DependencyType] +# TODO this is going away (probably) @dataclass(frozen=True) class _AccessRelation: """A class that stores information about a particular array access in a @@ -77,8 +80,8 @@ class _AccessRelation: relation: isl.Map access_type: AccessType -def generate_dependency_relations(knl: LoopKernel) \ - -> tuple[list[HappensBefore], list[HappensBefore], list[HappensBefore]]: +def generate_dependency_relations(knl: LoopKernel) -> \ + tuple[list[HappensBefore], list[HappensBefore], list[HappensBefore]]: """Generates :class:`isl.Map` objects representing the data dependencies between statements in a loopy program. The :class:`isl.Map` objects are stored in a :class:`loopy.Dependency.HappensBefore` object along with the dependee id, @@ -112,6 +115,10 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: return dependency + # TODO do not compute each dependency type separately, there is no need. + # instead, compute all dependencies at once so we do not need to unionize + # them later, i.e., in `generate_execution_order`. + read_maps: list[_AccessRelation] = [_AccessRelation(insn.id, var, get_map(var, insn), AccessType.READ) @@ -148,7 +155,7 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: for write2 in write_maps if write1.variable_name == write2.variable_name] - return write_read, read_write, write_write + return write_read, read_write, write_write def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: """Generate the "happens-before" execution order that *must* be respected by From d35f11b5fb10d58c0369ce3b0d47100fba4f5ab4 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sat, 5 Nov 2022 20:04:45 -0500 Subject: [PATCH 17/19] Changes in the way dependencies are computed --- loopy/kernel/dependency.py | 108 ++++++++++--------------------------- 1 file changed, 27 insertions(+), 81 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 07d44a6cd..2ce30b226 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -9,21 +9,6 @@ from dataclasses import dataclass from enum import Enum -# TODO this is going away -class DependencyType(Enum): - """An enumeration of the types of data dependencies found in a program. - """ - WRITE_AFTER_READ = 0 - READ_AFTER_WRITE = 1 - WRITE_AFTER_WRITE = 2 - -class AccessType(Enum): - """An enumeration of the types of accesses made by statements in a program. - """ - READ = 0 - WRITE = 1 - -# TODO this might be going away @dataclass(frozen=True) class HappensBefore: """A class representing a "happens-before" relationship between two @@ -42,18 +27,13 @@ class HappensBefore: An :class:`isl.Map` representing the data dependency. The input of the map is an iname tuple and the output of the map is a set of iname tuples that must execute after the input. - - .. attribute:: dependency_type - A :class:`DependencyType` of :class:`Enum` representing the dependency - type (write-read, read-write, write-write). """ happens_before: str variable_name: Optional[str] relation: isl.Map - dependency_type: Optional[DependencyType] -# TODO this is going away (probably) +# TODO Do we really need this? @dataclass(frozen=True) class _AccessRelation: """A class that stores information about a particular array access in a @@ -78,10 +58,8 @@ class _AccessRelation: id: str variable_name: str relation: isl.Map - access_type: AccessType -def generate_dependency_relations(knl: LoopKernel) -> \ - tuple[list[HappensBefore], list[HappensBefore], list[HappensBefore]]: +def generate_dependency_relations(knl: LoopKernel) -> list[HappensBefore]: """Generates :class:`isl.Map` objects representing the data dependencies between statements in a loopy program. The :class:`isl.Map` objects are stored in a :class:`loopy.Dependency.HappensBefore` object along with the dependee id, @@ -94,7 +72,7 @@ def generate_dependency_relations(knl: LoopKernel) -> \ objects describing the data dependencies. """ bmap: BatchedAccessMapMapper = BatchedAccessMapMapper(knl, - knl.all_variable_names()) + knl.all_variable_names()) for insn in knl.instructions: bmap(insn.assignee, insn.within_inames) bmap(insn.expression, insn.within_inames) @@ -102,6 +80,9 @@ def generate_dependency_relations(knl: LoopKernel) -> \ def get_map(var: str, insn: InstructionBase) -> isl.Map: return bmap.access_maps[var][insn.within_inames] + def get_var_list(insn: InstructionBase) -> frozenset[str]: + return read_var_list(insn) | write_var_list(insn) + def read_var_list(insn: InstructionBase) -> frozenset[str]: return insn.read_dependency_names() - insn.within_inames @@ -115,47 +96,21 @@ def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: return dependency - # TODO do not compute each dependency type separately, there is no need. - # instead, compute all dependencies at once so we do not need to unionize - # them later, i.e., in `generate_execution_order`. - - read_maps: list[_AccessRelation] = [_AccessRelation(insn.id, var, - get_map(var, insn), - AccessType.READ) - for insn in knl.instructions - for var in read_var_list(insn)] - write_maps: list[_AccessRelation] = [_AccessRelation(insn.id, var, - get_map(var, insn), - AccessType.WRITE) - for insn in knl.instructions - for var in write_var_list(insn)] - - write_read: list[HappensBefore] = [HappensBefore(read.id, - write.variable_name, - get_dependency_relation(write.relation, - read.relation), - DependencyType.WRITE_AFTER_READ) - for write in write_maps - for read in read_maps - if write.variable_name == read.variable_name] - read_write: list[HappensBefore] = [HappensBefore(write.id, - read.variable_name, - get_dependency_relation(read.relation, - write.relation), - DependencyType.READ_AFTER_WRITE) - for read in read_maps - for write in write_maps - if read.variable_name == write.variable_name] - write_write: list[HappensBefore] = [HappensBefore(write2.id, - write1.variable_name, - get_dependency_relation(write1.relation, - write2.relation), - DependencyType.WRITE_AFTER_WRITE) - for write1 in write_maps - for write2 in write_maps - if write1.variable_name == write2.variable_name] - - return write_read, read_write, write_write + # TODO can we do this without computing access maps and storing them? + accesses: list[_AccessRelation] = [_AccessRelation(insn.id, var, + get_map(var, insn)) + for insn in knl.instructions + for var in get_var_list(insn)] + + dependencies: list[HappensBefore] = [HappensBefore(dependent.id, + dependee.variable_name, + get_dependency_relation(dependent.relation, + dependee.relation)) + for dependent in accesses + for dependee in accesses + if dependent.variable_name == dependee.variable_name] + + return dependencies def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: """Generate the "happens-before" execution order that *must* be respected by @@ -169,26 +124,17 @@ def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: execution required by the dependencies in a loopy program. """ - write_read, read_write, write_write = generate_dependency_relations(knl) - + dependencies: list[HappensBefore] = generate_dependency_relations(knl) execution_order: frozenset[isl.Map] = frozenset() - for insn in knl.instructions: + # TODO replace lexicographical domain with existing happens before domain: isl.BasicSet = knl.get_inames_domain(insn.within_inames) - insn_order: isl.Map = domain.lex_lt_set(domain) - - union_of_dependencies: isl.Map = reduce(lambda x, - y: x.relation | y.relation, - write_read) - union_of_dependencies |= reduce(lambda x, y: x.relation | y.relation, - read_write) - union_of_dependencies |= reduce(lambda x, y: x.relation | y.relation, - write_write) - - insn_order: isl.Map = insn_order & union_of_dependencies + insn_order: isl.Map = domain.lex_lt_set(domain) & \ + reduce(lambda x, y: x | y, [dep.relation for dep in + dependencies]) execution_order = execution_order | frozenset({insn_order}) - return execution_order + return execution_order def verify_execution_order(knl: LoopKernel, existing_happens_before: isl.Map): """Verify that a given transformation respects the dependencies in a From a3a4c15dd73ba96195e3b8861bfded9fb9969362 Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Sun, 6 Nov 2022 11:43:46 -0600 Subject: [PATCH 18/19] Using itertools.product instead of doubly nested for loop in computing dependencies --- loopy/kernel/dependency.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 2ce30b226..98d5b6e34 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -4,10 +4,10 @@ from loopy import LoopKernel from loopy import InstructionBase +from itertools import product from functools import reduce from typing import Optional from dataclasses import dataclass -from enum import Enum @dataclass(frozen=True) class HappensBefore: @@ -80,34 +80,34 @@ def generate_dependency_relations(knl: LoopKernel) -> list[HappensBefore]: def get_map(var: str, insn: InstructionBase) -> isl.Map: return bmap.access_maps[var][insn.within_inames] - def get_var_list(insn: InstructionBase) -> frozenset[str]: - return read_var_list(insn) | write_var_list(insn) - - def read_var_list(insn: InstructionBase) -> frozenset[str]: + def read_variables(insn: InstructionBase) -> frozenset[str]: return insn.read_dependency_names() - insn.within_inames - def write_var_list(insn: InstructionBase) -> frozenset[str]: + def write_variables(insn: InstructionBase) -> frozenset[str]: return insn.write_dependency_names() - insn.within_inames - def get_dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: + def variable_list(insn: InstructionBase) -> frozenset[str]: + return read_variables(insn) | write_variables(insn) + + def dependency_relation(x: isl.Map, y:isl.Map) -> isl.Map: dependency: isl.Map = x.apply_range(y.reverse()) diagonal: isl.Map = dependency.identity(dependency.get_space()) dependency -= diagonal return dependency - # TODO can we do this without computing access maps and storing them? + # TODO can we reduce this code even further by not computing access + # relations before we begin computing dependencies? accesses: list[_AccessRelation] = [_AccessRelation(insn.id, var, get_map(var, insn)) for insn in knl.instructions - for var in get_var_list(insn)] + for var in variable_list(insn)] dependencies: list[HappensBefore] = [HappensBefore(dependent.id, dependee.variable_name, - get_dependency_relation(dependent.relation, - dependee.relation)) - for dependent in accesses - for dependee in accesses + dependency_relation(dependent.relation, + dependee.relation)) + for dependee, dependent in product(*[accesses,accesses]) if dependent.variable_name == dependee.variable_name] return dependencies @@ -132,7 +132,7 @@ def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: insn_order: isl.Map = domain.lex_lt_set(domain) & \ reduce(lambda x, y: x | y, [dep.relation for dep in dependencies]) - execution_order = execution_order | frozenset({insn_order}) + execution_order: frozenset[isl.Map] = execution_order | frozenset({insn_order}) return execution_order From 4d4ebdb810e37ff1762281ff29184c3c9d964dde Mon Sep 17 00:00:00 2001 From: "Addison J. Alvey-Blanco" Date: Mon, 7 Nov 2022 14:20:20 -0600 Subject: [PATCH 19/19] Small changes --- loopy/kernel/dependency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/kernel/dependency.py b/loopy/kernel/dependency.py index 98d5b6e34..d8584bde2 100644 --- a/loopy/kernel/dependency.py +++ b/loopy/kernel/dependency.py @@ -127,7 +127,7 @@ def generate_execution_order(knl: LoopKernel) -> frozenset[isl.Map]: dependencies: list[HappensBefore] = generate_dependency_relations(knl) execution_order: frozenset[isl.Map] = frozenset() for insn in knl.instructions: - # TODO replace lexicographical domain with existing happens before + # TODO replace lexicographical ordering with existing happens before domain: isl.BasicSet = knl.get_inames_domain(insn.within_inames) insn_order: isl.Map = domain.lex_lt_set(domain) & \ reduce(lambda x, y: x | y, [dep.relation for dep in