Skip to content

Commit

Permalink
Merge pull request #2109 from devitocodes/mpi-moar
Browse files Browse the repository at this point in the history
mpi: Packed gathers and scatters
  • Loading branch information
mloubout committed May 2, 2023
2 parents e6f25d5 + c9d23df commit bcc6b4b
Show file tree
Hide file tree
Showing 13 changed files with 403 additions and 183 deletions.
2 changes: 1 addition & 1 deletion devito/ir/iet/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def iet_build(stree):
nsections += 1

elif i.is_Halo:
body = HaloSpot(i.halo_scheme, body=queues.pop(i))
body = HaloSpot(queues.pop(i), i.halo_scheme)

elif i.is_Sync:
body = SyncSpot(i.sync_ops, body=queues.pop(i))
Expand Down
6 changes: 4 additions & 2 deletions devito/ir/iet/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1372,9 +1372,9 @@ class HaloSpot(Node):

_traversable = ['body']

def __init__(self, halo_scheme, body=None):
def __init__(self, body, halo_scheme):
super(HaloSpot, self).__init__()
self._halo_scheme = halo_scheme

if isinstance(body, Node):
self._body = body
elif isinstance(body, (list, tuple)) and len(body) == 1:
Expand All @@ -1384,6 +1384,8 @@ def __init__(self, halo_scheme, body=None):
else:
raise ValueError("`body` is expected to be a single Node")

self._halo_scheme = halo_scheme

def __repr__(self):
functions = "(%s)" % ",".join(i.name for i in self.functions)
return "<%s%s>" % (self.__class__.__name__, functions)
Expand Down
140 changes: 91 additions & 49 deletions devito/ir/support/basic.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from itertools import chain
from itertools import chain, product

from cached_property import cached_property
from sympy import S
Expand Down Expand Up @@ -482,20 +482,20 @@ def touched_halo(self, findex):
return (touch_halo_left, touch_halo_right)


class Dependence(object):
class Relation(object):

"""
A data dependence between two TimedAccess objects.
A relation between two TimedAccess objects.
"""

def __init__(self, source, sink):
assert isinstance(source, TimedAccess) and isinstance(sink, TimedAccess)
assert source.function is sink.function
assert source.function == sink.function
self.source = source
self.sink = sink

def __repr__(self):
return "%s -> %s" % (self.source, self.sink)
return "%s -- %s" % (self.source, self.sink)

def __eq__(self, other):
# If the timestamps are equal in `self` (ie, an inplace dependence) then
Expand Down Expand Up @@ -534,6 +534,67 @@ def distance_mapper(self):
retval[d] = j
return retval

@cached_property
def is_regular(self):
# NOTE: what we do below is stronger than something along the lines of
# `self.source.is_regular and self.sink.is_regular`
# `source` and `sink` may be regular in isolation, but the relation
# itself could be irregular, as the two TimedAccesses may stem from
# different iteration spaces. Instead if the distance is an integer
# vector, it is guaranteed that the iteration space is the same
return all(is_integer(i) for i in self.distance)

@cached_property
def is_irregular(self):
return not self.is_regular

@cached_property
def is_lex_positive(self):
"""
True if the source preceeds the sink, False otherwise.
"""
return self.source.timestamp < self.sink.timestamp

@cached_property
def is_lex_equal(self):
"""
True if the source has same timestamp as the sink, False otherwise.
"""
return self.source.timestamp == self.sink.timestamp

@cached_property
def is_lex_negative(self):
"""
True if the sink preceeds the source, False otherwise.
"""
return self.source.timestamp > self.sink.timestamp

@cached_property
def is_lex_non_stmt(self):
"""
True if either the source or the sink are from non-statements,
False otherwise.
"""
return self.source.timestamp == -1 or self.sink.timestamp == -1

@property
def is_local(self):
return self.function.is_Symbol

@property
def is_imaginary(self):
return S.ImaginaryUnit in self.distance


class Dependence(Relation):

"""
A data dependence between two TimedAccess objects.
"""

def __repr__(self):
return "%s -> %s" % (self.source, self.sink)

@cached_property
def cause(self):
"""Return the findex causing the dependence."""
Expand Down Expand Up @@ -585,50 +646,6 @@ def is_iaw(self):
def is_reduction(self):
return self.source.is_reduction or self.sink.is_reduction

@cached_property
def is_regular(self):
# Note: what we do below is stronger than something along the lines of
# `self.source.is_regular and self.sink.is_regular`
# `source` and `sink` may be regular in isolation, but the dependence
# itself could be irregular, as the two TimedAccesses may stem from
# different iteration spaces. Instead if the distance is an integer
# vector, it is guaranteed that the iteration space is the same
return all(is_integer(i) for i in self.distance)

@cached_property
def is_irregular(self):
return not self.is_regular

@cached_property
def is_lex_positive(self):
"""True if the source preceeds the sink, False otherwise."""
return self.source.timestamp < self.sink.timestamp

@cached_property
def is_lex_equal(self):
"""True if the source has same timestamp as the sink, False otherwise."""
return self.source.timestamp == self.sink.timestamp

@cached_property
def is_lex_negative(self):
"""True if the sink preceeds the source, False otherwise."""
return self.source.timestamp > self.sink.timestamp

@cached_property
def is_lex_non_stmt(self):
"""
True if either the source or the sink are from non-statements, False otherwise.
"""
return self.source.timestamp == -1 or self.sink.timestamp == -1

@property
def is_local(self):
return self.function.is_Symbol

@property
def is_imaginary(self):
return S.ImaginaryUnit in self.distance

@memoized_meth
def is_const(self, dim):
"""
Expand Down Expand Up @@ -1013,3 +1030,28 @@ def d_from_access(self, accesses):
TimedAccess objects.
"""
return DependenceGroup(self.d_from_access_gen(accesses))

@memoized_generator
def r_gen(self):
"""
Generate the Relations of the Scope.
"""
for f in self.functions:
v = self.reads.get(f, []) + self.writes.get(f, [])

for a0, a1 in product(v, v):
if a0 is a1:
continue

r = Relation(a0, a1)
if r.is_imaginary:
continue

yield r

@cached_property
def r_all(self):
"""
All Relations of the Scope.
"""
return list(self.r_gen())
18 changes: 14 additions & 4 deletions devito/mpi/halo_scheme.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,20 +362,30 @@ def arguments(self):

def project(self, functions):
"""
Create a new HaloScheme which only retains the HaloSchemeEntries corresponding
to the provided ``functions``.
Create a new HaloScheme that only retains the HaloSchemeEntries corresponding
to the provided `functions`.
"""
fmapper = {f: v for f, v in self.fmapper.items() if f in as_tuple(functions)}
return HaloScheme.build(fmapper, self.honored)

def drop(self, functions):
"""
Create a new HaloScheme which contains all entries in ``self`` except those
corresponding to the provided ``functions``.
Create a new HaloScheme that contains all entries in `self` except those
corresponding to the provided `functions`.
"""
fmapper = {f: v for f, v in self.fmapper.items() if f not in as_tuple(functions)}
return HaloScheme.build(fmapper, self.honored)

def add(self, f, hse):
"""
Create a new HaloScheme that contains all entries in `self` plus the one
passed in input. If `f` already exists in `self`, the old value is
overridden.
"""
fmapper = dict(self.fmapper.items())
fmapper[f] = hse
return HaloScheme.build(fmapper, self.honored)


def classify(exprs, ispace):
"""
Expand Down
Loading

0 comments on commit bcc6b4b

Please sign in to comment.