Skip to content

Commit a0ca4d3

Browse files
committed
compiler: Improve DDA involving SubDimensions
1 parent 418f94a commit a0ca4d3

2 files changed

Lines changed: 175 additions & 0 deletions

File tree

‎devito/ir/support/basic.py‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,14 @@ def distance(self, other, logical=False):
369369
# E.g., `self=R<f,[x]>` and `self.itintervals=(x, i)`
370370
break
371371

372+
# If over SubDimensions, check disjointness
373+
test = disjoint_subdims(self[n], other[n], sai, oai, sit, oit)
374+
if test == DISJOINT:
375+
return Vector(S.ImaginaryUnit)
376+
elif test == MAYBE_OVERLAP:
377+
ret.append(S.Infinity)
378+
continue
379+
372380
try:
373381
if not (sit == oit and sai.root is oai.root):
374382
# E.g., `self=R<f,[x + 2]>` and `other=W<f,[i + 1]>`
@@ -1527,6 +1535,92 @@ def skippable_interval(d, ispace, it):
15271535
return d is None or (d in ispace and not d._defines & it.dim._defines)
15281536

15291537

1538+
# Possible return values for `disjoint_subdims`
1539+
INAPPLICABLE = 0
1540+
DISJOINT = 1
1541+
MAYBE_OVERLAP = 2
1542+
1543+
1544+
def disjoint_subdims(e0, e1, d0, d1, it0, it1):
1545+
"""
1546+
Determine whether two accesses span distinct pieces of the same
1547+
SubDimension decomposition.
1548+
1549+
Consider a root Dimension `x` with bounds `x_m` and `x_M`. A valid
1550+
left/middle/right decomposition with thicknesses `L` and `R` is::
1551+
1552+
xl = [x_m, x_m + L - 1]
1553+
xm = [x_m + L, x_M - R]
1554+
xr = [x_M - R + 1, x_M]
1555+
1556+
These intervals are pairwise disjoint. Replacing `xl`, `xm`, or `xr`
1557+
with `x` in an affine access removes the choice of partition piece while
1558+
retaining the relative access. If two such normalized accesses have zero
1559+
distance, they apply the same affine map to disjoint intervals and therefore
1560+
cannot refer to the same data point. The apparent dependence is imaginary.
1561+
1562+
For example, `f[xl]` and `f[xm]` normalize to `f[x]` and `f[x]`;
1563+
they are independent. The same holds for `f[xl + 1]` and `f[xm + 1]`
1564+
when their iteration intervals have equal offsets. By contrast, `f[xl]`
1565+
and `f[xm - 1]` normalize to different accesses, and the latter may reach
1566+
into the left piece, so they must be treated conservatively.
1567+
1568+
This proof requires distinct pieces of the same root, compatible declared
1569+
thicknesses, affine accesses, and iteration intervals with equal offsets and
1570+
directions. Runtime bounds are assumed to preserve the declared partition.
1571+
Return DISJOINT if disjointness is proven, and MAYBE_OVERLAP if the
1572+
intervals are aligned SubDimensions but are not proven disjoint. In
1573+
particular, two declarations of the same left, right, or middle piece
1574+
overlap along this Dimension. MAYBE_OVERLAP lets the caller record an
1575+
infinite distance and inspect later Dimensions, which may still prove the
1576+
multidimensional accesses disjoint. Return INAPPLICABLE if this test does not
1577+
apply, so that the general distance analysis can classify the dependence.
1578+
"""
1579+
try:
1580+
# E.g., `f[xl]` over `(xl,)` and `f[xm]` over `(xm,)` need this
1581+
# special test, while accesses over the same `(xl,)` should use general
1582+
# distance analysis, so we can return immediately in such a case
1583+
if not (d0.is_Sub and
1584+
d1.is_Sub and
1585+
d0.root is d1.root and
1586+
it0.dim.root is d0.root and
1587+
it1.dim.root is d1.root and
1588+
it0 != it1):
1589+
return INAPPLICABLE
1590+
except AttributeError:
1591+
return INAPPLICABLE
1592+
1593+
if (d0.is_left and d1.is_middle) or \
1594+
(d0.is_middle and d1.is_left):
1595+
is_partition = d0.ltkn.value == d1.ltkn.value
1596+
elif (d0.is_middle and d1.is_right) or \
1597+
(d0.is_right and d1.is_middle):
1598+
is_partition = d0.rtkn.value == d1.rtkn.value
1599+
elif d0.is_left and d1.is_right:
1600+
is_partition = d0.ltkn.value is not None and d1.rtkn.value is not None
1601+
elif d0.is_right and d1.is_left:
1602+
is_partition = d0.rtkn.value is not None and d1.ltkn.value is not None
1603+
else:
1604+
is_partition = False
1605+
1606+
if not is_partition:
1607+
return MAYBE_OVERLAP
1608+
1609+
if not q_affine(e0, d0) or not q_affine(e1, d1):
1610+
return MAYBE_OVERLAP
1611+
1612+
if it0.offsets != it1.offsets or it0.direction is not it1.direction:
1613+
return MAYBE_OVERLAP
1614+
1615+
e0 = e0._subs(d0, d0.root)
1616+
e1 = e1._subs(d1, d1.root)
1617+
1618+
if e0 - e1 == 0:
1619+
return DISJOINT
1620+
else:
1621+
return MAYBE_OVERLAP
1622+
1623+
15301624
def disjoint_test(e0, e1, d, it):
15311625
"""
15321626
A rudimentary test to check if two accesses `e0` and `e1` along `d` within

‎tests/test_ir.py‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,87 @@ def test_timed_access_cmp(self, ta_literal):
357357
assert tcyx_irr0 >= tcyx_irr0
358358
assert tcyx_irr0 == tcyx_irr0
359359

360+
def test_timed_access_distance_subdimensions(self):
361+
grid = Grid(shape=(24, 24))
362+
x, y = grid.dimensions
363+
364+
xl = SubDimension.left('xl', x, 4)
365+
xm = SubDimension.middle('xm', x, 4, 4)
366+
xr = SubDimension.right('xr', x, 4)
367+
xm_bad = SubDimension.middle('xm_bad', x, 3, 4)
368+
yl = SubDimension.left('yl', y, 4)
369+
ym = SubDimension.middle('ym', y, 4, 4)
370+
xl_overlap = SubDimension.left('xl_overlap', x, 4)
371+
xm_overlap = SubDimension.middle('xm_overlap', x, 4, 4)
372+
xr_overlap = SubDimension.right('xr_overlap', x, 4)
373+
374+
f = Function(name='f', grid=grid)
375+
376+
left = TimedAccess(
377+
f[xl, y], 'W', 0, IterationSpace([Interval(xl), Interval(y)])
378+
)
379+
middle = TimedAccess(
380+
f[xm, y], 'R', 1, IterationSpace([Interval(xm), Interval(y)])
381+
)
382+
right = TimedAccess(
383+
f[xr, y], 'R', 1, IterationSpace([Interval(xr), Interval(y)])
384+
)
385+
left_overlap = TimedAccess(
386+
f[xl_overlap, y], 'R', 1,
387+
IterationSpace([Interval(xl_overlap), Interval(y)])
388+
)
389+
middle_overlap = TimedAccess(
390+
f[xm_overlap, y], 'R', 1,
391+
IterationSpace([Interval(xm_overlap), Interval(y)])
392+
)
393+
right_overlap = TimedAccess(
394+
f[xr_overlap, y], 'R', 1,
395+
IterationSpace([Interval(xr_overlap), Interval(y)])
396+
)
397+
bad = TimedAccess(
398+
f[xm_bad, y], 'R', 1,
399+
IterationSpace([Interval(xm_bad), Interval(y)])
400+
)
401+
shifted = TimedAccess(
402+
f[xm + 1, y], 'R', 1,
403+
IterationSpace([Interval(xm), Interval(y)])
404+
)
405+
shifted_range = TimedAccess(
406+
f[xm, y], 'R', 1,
407+
IterationSpace([Interval(xm, 1, 1), Interval(y)])
408+
)
409+
left_nonlinear = TimedAccess(
410+
f[xl % 2, y], 'W', 0, IterationSpace([Interval(xl), Interval(y)])
411+
)
412+
middle_nonlinear = TimedAccess(
413+
f[xm % 2, y], 'R', 1, IterationSpace([Interval(xm), Interval(y)])
414+
)
415+
orthogonal = TimedAccess(
416+
f[x, yl], 'R', 1, IterationSpace([Interval(x), Interval(yl)])
417+
)
418+
corner_left = TimedAccess(
419+
f[xl, yl], 'W', 0, IterationSpace([Interval(xl), Interval(yl)])
420+
)
421+
corner_middle = TimedAccess(
422+
f[xl_overlap, ym], 'R', 1,
423+
IterationSpace([Interval(xl_overlap), Interval(ym)])
424+
)
425+
426+
assert left.distance(middle) == (S.ImaginaryUnit,)
427+
assert middle.distance(right) == (S.ImaginaryUnit,)
428+
assert left.distance(right) == (S.ImaginaryUnit,)
429+
assert right.distance(left) == (S.ImaginaryUnit,)
430+
assert corner_left.distance(corner_middle) == (S.ImaginaryUnit,)
431+
432+
assert left.distance(left_overlap) == (S.Infinity, 0)
433+
assert middle.distance(middle_overlap) == (S.Infinity, 0)
434+
assert right.distance(right_overlap) == (S.Infinity, 0)
435+
assert left.distance(bad) == (S.Infinity, 0)
436+
assert left.distance(shifted) == (S.Infinity, 0)
437+
assert left.distance(shifted_range) == (S.Infinity, 0)
438+
assert left_nonlinear.distance(middle_nonlinear) == (S.Infinity, 0)
439+
assert left.distance(orthogonal) == (S.Infinity,)
440+
360441

361442
class TestSpace:
362443

0 commit comments

Comments
 (0)