-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dependency finding #704
base: main
Are you sure you want to change the base?
Dependency finding #704
Changes from 8 commits
9f097a3
779c2f0
365781d
6a58e1d
ba3fbf5
990624b
93b4126
01d3688
51c460d
f6b75dd
8327c70
999446f
f110b30
a671762
34f5b90
adb924c
a7eb364
c23eff4
1647d5a
055d7e3
888291f
b785738
457cb6f
096e68b
1b9f186
5f3a249
169e808
faa5f23
d3224d1
a215511
72ab7e7
590a8cf
5208fb6
cf4b9bd
faff25c
d967949
307aa83
808cef6
8a28e7f
5731025
e745336
b9cc691
d361167
16be472
8e0e384
69265b2
8c42f69
970feb0
5b98c84
2043233
1cea1b3
9131461
290c91e
7cd7ea0
c1a553a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ htmlcov | |
lextab.py | ||
yacctab.py | ||
.pytest_cache/* | ||
.DS_STORE | ||
|
||
loopy/_git_rev.py | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
# FIXME Add copyright header | ||
|
||
|
||
import islpy as isl | ||
from islpy import dim_type | ||
import pymbolic.primitives as p | ||
|
||
from dataclasses import dataclass | ||
from islpy import Map | ||
from typing import Optional | ||
|
||
from loopy import LoopKernel | ||
from loopy.symbolic import WalkMapper | ||
from loopy.translation_unit import for_each_kernel | ||
from loopy.typing import ExpressionT | ||
|
||
@dataclass(frozen=True) | ||
class HappensAfter: | ||
variable_name: Optional[str] | ||
instances_rel: Optional[Map] | ||
|
||
class AccessMapMapper(WalkMapper): | ||
""" | ||
TODO Update this documentation so it reflects proper formatting | ||
|
||
Used instead of BatchedAccessMapMapper to get single access maps for each | ||
instruction. | ||
""" | ||
|
||
def __init__(self, kernel: LoopKernel, var_names: set): | ||
self.kernel = kernel | ||
self._var_names = var_names | ||
|
||
from collections import defaultdict | ||
self.access_maps = defaultdict(lambda: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document what the keys/values of these mappings are. |
||
defaultdict(lambda: | ||
defaultdict(lambda: None))) | ||
|
||
super.__init__() | ||
|
||
def map_subscript(self, expr: ExpressionT, inames: frozenset, insn_id: str): | ||
|
||
domain = self.kernel.get_inames_domain(inames) | ||
|
||
WalkMapper.map_subscript(self, expr, inames) | ||
|
||
assert isinstance(expr.aggregate, p.Variable) | ||
|
||
if expr.aggregate.name not in self._var_names: | ||
return | ||
|
||
arg_name = expr.aggregate.name | ||
subscript = expr.index_tuple | ||
|
||
from loopy.diagnostic import UnableToDetermineAccessRangeError | ||
from loopy.symbolic import get_access_map | ||
|
||
try: | ||
access_map = get_access_map(domain, subscript) | ||
except UnableToDetermineAccessRangeError: | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the conservative approach? |
||
|
||
if self.access_maps[insn_id][arg_name][inames] is None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the |
||
self.access_maps[insn_id][arg_name][inames] = access_map | ||
|
||
def compute_happens_after(knl: LoopKernel) -> LoopKernel: | ||
""" | ||
TODO Update documentation to reflect the proper format. | ||
|
||
Determine dependency relations that exist between instructions. Similar to | ||
apply_single_writer_dependency_heuristic. Extremely rough draft. | ||
""" | ||
writer_map = knl.writer_map() | ||
variables = knl.all_variable_names - knl.inames.keys() | ||
|
||
# initialize the mapper | ||
amap = AccessMapMapper(knl, variables) | ||
|
||
for insn in knl.instructions: | ||
amap(insn.assignee, insn.within_inames) | ||
amap(insn.expression, insn.within_inames) | ||
|
||
# compute data dependencies | ||
dep_map = { | ||
insn.id: insn.read_dependency_names() - insn.within_inames | ||
for insn in knl.instructions | ||
} | ||
|
||
new_insns = [] | ||
for insn in knl.instructions: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think about transitive dependencies There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
current_insn = insn.id | ||
inames = insn.within_inames | ||
|
||
new_happens_after = [] | ||
for var in dep_map[insn.id]: | ||
for writer in (writer_map.get(var, set()) - { current_insn }): | ||
|
||
# get relation for current instruction and a write instruction | ||
cur_relation = amap.access_maps[current_insn][var][inames] | ||
write_relation = amap.access_maps[writer][var][inames] | ||
|
||
# compute the dependency relation | ||
dep_relation = cur_relation.apply_range(write_relation.reverse()) | ||
|
||
# create the mapping from writer -> (variable, dependency rel'n) | ||
happens_after = HappensAfter(var, dep_relation) | ||
happens_after_mapping = { writer: happens_after } | ||
|
||
# add to the new list of dependencies | ||
new_happens_after |= happens_after_mapping | ||
|
||
# update happens_after of our current instruction with the mapping | ||
insn = insn.copy(happens_after=new_happens_after) | ||
new_insns.append(insn) | ||
|
||
# return the kernel with the new instructions | ||
return knl.copy(instructions=new_insns) | ||
|
||
def add_lexicographic_happens_after_orig(knl: LoopKernel) -> None: | ||
""" | ||
TODO properly format this documentation. | ||
|
||
Creates a dependency relation between two instructions based on a | ||
lexicographic ordering of the statements in a program. | ||
|
||
For example, the C-like execution order (i.e. sequential ordering) of a | ||
program. | ||
""" | ||
|
||
# we want to modify the output dimension and OUT = 3 | ||
dim_type = isl.dim_type.out | ||
|
||
# generate an unordered mapping from statement instances to points in the | ||
# loop domain | ||
insn_number = 0 | ||
schedules = {} | ||
for insn in knl.instructions: | ||
domain = knl.get_inames_domain(insn.within_inames) | ||
|
||
# if we do not set the dim name, the name is set as None | ||
domain = domain.insert_dims(dim_type, 0, 1).set_dim_name(dim_type, 0, | ||
insn.id) | ||
|
||
space = domain.get_space() | ||
domain = domain.add_constraint( | ||
isl.Constraint.eq_from_names(space, {1: -1*insn_number, insn.id: 1}) | ||
) | ||
|
||
# this may not be the final way we keep track of the schedules | ||
schedule = isl.Map.from_domain_and_range(domain, domain) | ||
schedules[insn.id] = schedule | ||
|
||
insn_number += 1 | ||
|
||
# determine a lexicographic order on the space the schedules belong to | ||
|
||
|
||
@for_each_kernel | ||
def add_lexicographic_happens_after(knl: LoopKernel) -> LoopKernel: | ||
|
||
new_insns = [] | ||
|
||
for iafter, insn_after in enumerate(knl.instructions): | ||
if iafter == 0: | ||
new_insns.append(insn_after) | ||
else: | ||
insn_before = knl.instructions[iafter - 1] | ||
shared_inames = insn_after.within_inames & insn_before.within_inames | ||
unshared_before = insn_before.within_inames | ||
|
||
domain_before = knl.get_inames_domain(insn_before.within_inames) | ||
domain_after = knl.get_inames_domain(insn_after.within_inames) | ||
|
||
happens_before = isl.Map.from_domain_and_range( | ||
domain_before, domain_after) | ||
for idim in range(happens_before.dim(dim_type.out)): | ||
happens_before = happens_before.set_dim_name( | ||
dim_type.out, idim, | ||
happens_before.get_dim_name(dim_type.out, idim) + "'") | ||
n_inames_before = happens_before.dim(dim_type.in_) | ||
happens_before_set = happens_before.move_dims( | ||
dim_type.out, 0, | ||
dim_type.in_, 0, | ||
n_inames_before).range() | ||
|
||
shared_inames_order_before = [ | ||
domain_before.get_dim_name(dim_type.out, idim) | ||
for idim in range(domain_before.dim(dim_type.out)) | ||
if domain_before.get_dim_name(dim_type.out, idim) | ||
in shared_inames] | ||
shared_inames_order_after = [ | ||
domain_after.get_dim_name(dim_type.out, idim) | ||
for idim in range(domain_after.dim(dim_type.out)) | ||
if domain_after.get_dim_name(dim_type.out, idim) | ||
in shared_inames] | ||
|
||
assert shared_inames_order_after == shared_inames_order_before | ||
shared_inames_order = shared_inames_order_after | ||
Comment on lines
+85
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a big unresolved question here, in terms of what nesting order we should use for the shared inames. Right now, this uses the axis order in the domains, however we also already do have @kaushikcfd, got an opinion? |
||
|
||
affs = isl.affs_from_space(happens_before_set.space) | ||
|
||
lex_set = isl.Set.empty(happens_before_set.space) | ||
for iinnermost, innermost_iname in enumerate(shared_inames_order): | ||
innermost_set = affs[innermost_iname].lt_set( | ||
affs[innermost_iname+"'"]) | ||
|
||
for outer_iname in shared_inames_order[:iinnermost]: | ||
innermost_set = innermost_set & ( | ||
affs[outer_iname].eq_set(affs[outer_iname + "'"])) | ||
|
||
lex_set = lex_set | innermost_set | ||
|
||
lex_map = isl.Map.from_range(lex_set).move_dims( | ||
dim_type.in_, 0, | ||
dim_type.out, 0, | ||
n_inames_before) | ||
|
||
happens_before = happens_before & lex_map | ||
|
||
pu.db | ||
|
||
new_insns.append(insn_after) | ||
|
||
return knl.copy(instructions=new_insns) | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider making this a
CombineMapper
? (In that case, you'd need to definecombine
)