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 14 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
14 changes: 14 additions & 0 deletions loopy/kernel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ class _BoundsRecord:
upper_bound_pw_aff: isl.PwAff
size: isl.PwAff

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

Choose a reason for hiding this comment

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

Add docstrings, specifying what variables are in the map, what the two ends of the map mean, what naming conventions are used.

Copy link
Owner

Choose a reason for hiding this comment

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

Add into documentation.

insn_id: str
var_name: str
relation: isl.Map

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

Choose a reason for hiding this comment

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

Should access relations be long-lived?

Copy link
Owner

Choose a reason for hiding this comment

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

"has-a happier than is-a"

access_type: str
Copy link
Owner

Choose a reason for hiding this comment

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

Does this want to be an enum?


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

Choose a reason for hiding this comment

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

Something should be named "happens-before"

dependent_id: str
Copy link
Owner

Choose a reason for hiding this comment

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

Make one end of the happens-before implicit by ownership---only one insn ID needs storing.

dependency_type: str
Copy link
Owner

Choose a reason for hiding this comment

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

Wants to be an enum, possibly.


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

Expand Down
208 changes: 208 additions & 0 deletions loopy/kernel/dependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
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):
Copy link
Owner

Choose a reason for hiding this comment

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

I'm unsure whether we'll want this, or whether we'll want all those union'd together.

"""An enumeration of the types of data dependencies found in a program.
"""
WRITE_READ = 0
READ_WRITE = 1
WRITE_WRITE = 2
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
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.
"""
READ = 0
WRITE = 1

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

.. 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
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
variable_name: str
relation: isl.Map
dependency_type: DependencyType
variable_name: Optional[str]
relation: isl.Map
dependency_type: Optional[DependencyType]

(But probably better to make a data-mediated dependency a separate type, maybe a subclass.)


@dataclass(frozen=True)
class AccessRelation:
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 AccessRelation:
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) \
-> 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:
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:
# 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

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]

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
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.Map] = 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)
Copy link
Owner

Choose a reason for hiding this comment

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

This shouldn't start from lexicographic, but rather from an already-existing happens-before.


# v FIXME: there must be a better way
Copy link
Owner

Choose a reason for hiding this comment

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

Think about transitive dependencies.

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

Choose a reason for hiding this comment

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

functools.reduce


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

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
execution_order = execution_order | frozenset({insn_order})

return execution_order
Copy link
Owner

Choose a reason for hiding this comment

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

Put this into the LoopKernel data structure.


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