Skip to content
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

New dependency finding #683

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3c01e0c
Temp commit
a-alveyblanc Sep 26, 2022
a5ee582
Updated kernel-level dependency finding mechanisms.
a-alveyblanc Oct 2, 2022
1a939a5
Merge branch 'inducer:main' into dep_finder
a-alveyblanc Oct 2, 2022
e553cce
Kernel now updates `depends_on` in line with the description of
a-alveyblanc Oct 3, 2022
ed6d03b
Merge branch 'dep_finder' of github.com:a-alveyblanc/loopy into dep_f…
a-alveyblanc Oct 3, 2022
a10d3db
New file created for dependencies, added type annotations, documentat…
a-alveyblanc Oct 10, 2022
81b26a0
Adjusted alignment so lines of code were not so far past 80 columns
a-alveyblanc Oct 10, 2022
09899bf
Stub in dep_finder function
a-alveyblanc Oct 10, 2022
464895f
Add documentation, begin fleshing out
a-alveyblanc Oct 15, 2022
d947d6b
Add documentation, begin fleshing out generate_execution_order
a-alveyblanc Oct 15, 2022
fdb5a01
Implemented draft version of generate_execution_order and stub in ver…
a-alveyblanc Oct 15, 2022
29ae7c0
Last commit does not reflect all changes made in that commit
a-alveyblanc Oct 15, 2022
0294761
Rewrote execution order generation function
a-alveyblanc Oct 16, 2022
6ed1cce
Minor changes to draft of generate_execution_order and to the documen…
a-alveyblanc Oct 16, 2022
ccfca54
Removed some things from kernel __init__.py that shouldn't be there
a-alveyblanc Oct 17, 2022
6e47dd0
Updated subtraction of diagonal from found dependency relation.
a-alveyblanc Nov 4, 2022
a0fd274
Replaced dependency union operation with a reduction. Can this be red…
a-alveyblanc Nov 5, 2022
2569006
Added various TODOs to mark changes
a-alveyblanc Nov 5, 2022
d35f11b
Changes in the way dependencies are computed
a-alveyblanc Nov 6, 2022
a3a4c15
Using itertools.product instead of doubly nested for loop in computin…
a-alveyblanc Nov 6, 2022
4d4ebdb
Small changes
a-alveyblanc Nov 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions loopy/kernel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ class _BoundsRecord:
upper_bound_pw_aff: isl.PwAff
size: isl.PwAff


PreambleGenerator = Callable[["PreambleInfo"], Iterator[Tuple[int, str]]]


@dataclass(frozen=True)
class LoopKernel(Taggable):
"""These correspond more or less directly to arguments of
Expand Down
149 changes: 149 additions & 0 deletions loopy/kernel/dependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import islpy as isl

from loopy.symbolic import BatchedAccessMapMapper
from loopy import LoopKernel
from loopy import InstructionBase

from itertools import product
from functools import reduce
from typing import Optional
from dataclasses import dataclass

@dataclass(frozen=True)
class HappensBefore:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class HappensBefore:
class HappensAfter:

"""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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. attribute:: happens_before
.. attribute:: after_id

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.
"""

happens_before: str
variable_name: Optional[str]
relation: isl.Map

# TODO Do we really need this?
@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

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,
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:
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_variables(insn: InstructionBase) -> frozenset[str]:
return insn.read_dependency_names() - insn.within_inames

def write_variables(insn: InstructionBase) -> frozenset[str]:
return insn.write_dependency_names() - insn.within_inames

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 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 variable_list(insn)]

dependencies: list[HappensBefore] = [HappensBefore(dependent.id,
dependee.variable_name,
dependency_relation(dependent.relation,
dependee.relation))
for dependee, dependent in product(*[accesses,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
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.
"""

dependencies: list[HappensBefore] = generate_dependency_relations(knl)
execution_order: frozenset[isl.Map] = frozenset()
for insn in knl.instructions:
# 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
dependencies])
execution_order: frozenset[isl.Map] = 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
: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.

:returns: True or false depending on whether the provided execution order
respects the dependencies in the loopy program.
"""
pass
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at James's prior work.

1 change: 0 additions & 1 deletion loopy/kernel/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,6 @@ def __setstate__(self, val):

# }}}


def _get_assignee_var_name(expr):
from pymbolic.primitives import Variable, Subscript, Lookup
from loopy.symbolic import LinearSubscript, SubArrayRef
Expand Down