Skip to content

Commit be6e4e2

Browse files
committed
compiler: fix linearization padding check
1 parent bf87b54 commit be6e4e2

13 files changed

Lines changed: 95 additions & 145 deletions

File tree

devito/finite_differences/interpolation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def interp_mapper(source, target, dims):
1414
"""
1515
mapper = {}
1616
for d in dims:
17+
if d.is_Time:
18+
continue
1719
try:
1820
s = source[d]
1921
t = target[d]

devito/mpi/routines.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,7 @@ def _make_copy(self, f, hse, key, swap=False):
367367
eqns.extend([Eq(d.symbolic_max, d.symbolic_size - 1) for d in bdims])
368368

369369
vd = CustomDimension(name='vd', symbolic_size=f.ncomp)
370-
buf = Array(name='buf', dimensions=[vd] + bdims, dtype=f.c0.dtype,
371-
padding=0)
370+
buf = Array(name='buf', dimensions=[vd] + bdims, dtype=f.c0.dtype)
372371

373372
mapper = dict(zip(dims, bdims, strict=True))
374373
findices = [
@@ -410,9 +409,9 @@ def _make_sendrecv(self, f, hse, key, **kwargs):
410409
bdims = [CustomDimension(name='vd', symbolic_size=f.ncomp)] + dims
411410

412411
bufg = Array(name='bufg', dimensions=bdims, dtype=f.c0.dtype,
413-
padding=0, liveness='eager')
412+
liveness='eager')
414413
bufs = Array(name='bufs', dimensions=bdims, dtype=f.c0.dtype,
415-
padding=0, liveness='eager')
414+
liveness='eager')
416415

417416
ofsg = [Symbol(name=f'og{d.root}') for d in f.dimensions]
418417
ofss = [Symbol(name=f'os{d.root}') for d in f.dimensions]

devito/passes/clusters/buffering.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,9 @@ def generate_buffers(clusters, key, sregistry, options, **kwargs):
376376
assert len(buffers) == 1, "Unexpected form of multi-level buffering"
377377
buffer, = buffers
378378
xd = buffer.indices[dim]
379+
# The new buffer is fed by `buffer`, so it inherits its padding
380+
# policy regardless of `f`'s
381+
extra_kwargs = {'is_autopaddable': buffer.is_autopaddable}
379382
else:
380383
size = infer_buffer_size(f, dim, clusters)
381384

@@ -395,6 +398,7 @@ def generate_buffers(clusters, key, sregistry, options, **kwargs):
395398
except KeyError:
396399
name = sregistry.make_name(prefix='db')
397400
xd = xds[(dim, size)] = BufferDimension(name, 0, size-1, size, dim)
401+
extra_kwargs = {}
398402

399403
# The buffer dimensions
400404
dimensions = list(f.dimensions)
@@ -406,7 +410,7 @@ def generate_buffers(clusters, key, sregistry, options, **kwargs):
406410
name = sregistry.make_name(prefix=f'{f.name}b')
407411
mapper[f] = cls(name=name, dimensions=dimensions, dtype=f.dtype,
408412
grid=f.grid, halo=f.halo,
409-
space='mapped', mapped=f, f=f)
413+
space='mapped', mapped=f, f=f, **extra_kwargs)
410414

411415
return mapper
412416

devito/passes/iet/linearization.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,7 @@ def key1(f, d):
7070
if f.is_regular:
7171
# For paddable objects the following holds:
7272
# `same dim + same halo + same padding_dtype => same (auto-)padding`
73-
if d is f.dimensions[-1]:
74-
# Only the last dimension is padded
75-
try:
76-
if f.padding == f.mapped.padding:
77-
# Padding set from the mapped Function
78-
# e.g. from buffering or fft temp array
79-
pad_key = f.mapped.__padding_dtype__
80-
else:
81-
pad_key = f.__padding_dtype__
82-
except AttributeError:
83-
pad_key = f.__padding_dtype__
84-
else:
85-
pad_key = None
73+
pad_key = f.__padding_dtype__ if d is f.dimensions[-1] else None
8674

8775
return (d, f._size_halo[d], pad_key)
8876
else:

devito/types/array.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ class Array(ArrayBasic):
9494
to ``np.float32``.
9595
halo : iterable of 2-tuples, optional
9696
The halo region of the object.
97-
padding : iterable of 2-tuples, optional
98-
The padding region of the object.
9997
liveness : str, optional
10098
The liveness of the object. Allowed values: 'eager', 'lazy'. Defaults
10199
to 'lazy'. Used to override `_mem_internal_eager` and `_mem_internal_lazy`.
@@ -163,20 +161,12 @@ def __dtype_setup__(cls, **kwargs):
163161
return kwargs.get('dtype', np.float32)
164162

165163
def __padding_setup__(self, **kwargs):
166-
padding = kwargs.get('padding')
167-
if padding is None:
168-
if self.is_autopaddable:
169-
padding = self.__padding_setup_smart__(**kwargs)
170-
else:
171-
padding = ((0, 0),)*self.ndim
172-
elif isinstance(padding, DimensionTuple):
173-
padding = tuple(padding[d] for d in self.dimensions)
174-
elif is_integer(padding):
175-
padding = tuple((0, padding) for _ in range(self.ndim))
176-
elif isinstance(padding, tuple) and len(padding) == self.ndim:
177-
padding = tuple((0, i) if is_integer(i) else i for i in padding)
164+
# `padding=` is never honored: derived from policy to avoid stride
165+
# inconsistencies between Functions sharing dimensions/halo
166+
if self.is_autopaddable:
167+
padding = self.__padding_setup_smart__(**kwargs)
178168
else:
179-
raise TypeError(f'`padding` must be int or {self.ndim}-tuple of ints')
169+
padding = ((0, 0),)*self.ndim
180170
return DimensionTuple(*padding, getters=self.dimensions)
181171

182172
@property
@@ -247,7 +237,42 @@ class MappedArrayMixin:
247237

248238

249239
class ArrayMapped(MappedArrayMixin, Array):
250-
is_autopaddable = True
240+
241+
__rkwargs__ = Array.__rkwargs__ + ('mapped', 'is_autopaddable')
242+
243+
def __init_finalize__(self, *args, **kwargs):
244+
self._mapped = kwargs.get('mapped')
245+
self._is_autopaddable = kwargs.get('is_autopaddable')
246+
super().__init_finalize__(*args, **kwargs)
247+
248+
@property
249+
def mapped(self):
250+
return self._mapped
251+
252+
@property
253+
def is_autopaddable(self):
254+
if self._is_autopaddable is not None:
255+
return self._is_autopaddable
256+
if self.mapped is None:
257+
return True
258+
return self.mapped.is_autopaddable
259+
260+
@property
261+
def __padding_dtype__(self):
262+
if not self.is_autopaddable:
263+
return None
264+
if self.mapped is not None:
265+
return self.mapped.__padding_dtype__
266+
return super().__padding_dtype__
267+
268+
@cached_property
269+
def _signature(self):
270+
# Exclude `mapped` so buf-reuse can dedup buffers across distinct
271+
# mapped Functions
272+
ret = [type(self), self.indices]
273+
attrs = set(self.__rkwargs__) - {'name', 'function', 'mapped'}
274+
ret.extend(getattr(self, i) for i in attrs)
275+
return frozenset(ret)
251276

252277

253278
class ArrayObject(ArrayBasic):

devito/types/basic.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ class AbstractFunction(sympy.Function, Basic, Pickable, Evaluable):
711711
effect if autopadding is disabled, which is the default behavior.
712712
"""
713713

714-
__rkwargs__ = ('name', 'dtype', 'grid', 'halo', 'padding', 'ghost',
714+
__rkwargs__ = ('name', 'dtype', 'grid', 'halo', 'ghost',
715715
'alias', 'space', 'function', 'is_transient', 'avg_mode')
716716

717717
__properties__ = ('is_const', 'is_transient')
@@ -743,17 +743,16 @@ def __new__(cls, *args, **kwargs):
743743
return function
744744

745745
# If dimensions have been replaced, then it is necessary to set `function`
746-
# to None. It may also be necessary to remove halo and padding so that
747-
# they are rebuilt with the new dimensions
746+
# to None. It may also be necessary to remove halo so that it is rebuilt
747+
# with the new dimensions
748748
if function is not None and function.dimensions != dimensions:
749749
function = kwargs['function'] = None
750-
for i in ('halo', 'padding'):
751-
if len(kwargs[i]) != len(dimensions):
752-
kwargs.pop(i)
753-
else:
754-
# Downcast from DimensionTuple so that the new `dimensions`
755-
# are used down the line
756-
kwargs[i] = tuple(kwargs[i])
750+
if len(kwargs['halo']) != len(dimensions):
751+
kwargs.pop('halo')
752+
else:
753+
# Downcast from DimensionTuple so that the new `dimensions`
754+
# are used down the line
755+
kwargs['halo'] = tuple(kwargs['halo'])
757756

758757
with sympy_mutex:
759758
# Go straight through Basic, thus bypassing caching and machinery
@@ -890,8 +889,10 @@ def __halo_setup__(self, **kwargs):
890889
halo = tuple(kwargs.get('halo', ((0, 0),)*self.ndim))
891890
return DimensionTuple(*halo, getters=self.dimensions)
892891

893-
def __padding_setup__(self, padding=None, **kwargs):
894-
padding = tuple(padding or ((0, 0),)*self.ndim)
892+
def __padding_setup__(self, **kwargs):
893+
# `padding=` is never honored: derived from policy to avoid stride
894+
# inconsistencies between Functions sharing dimensions/halo
895+
padding = ((0, 0),)*self.ndim
895896
return DimensionTuple(*padding, getters=self.dimensions)
896897

897898
@cached_property

devito/types/dense.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,9 +1013,6 @@ class Function(DiscreteFunction):
10131013
Controller for memory allocation. To be used, for example, when one wants
10141014
to take advantage of the memory hierarchy in a NUMA architecture. Refer to
10151015
`default_allocator.__doc__` for more information.
1016-
padding : int or tuple of ints, optional
1017-
Allocate extra grid points to maximize data access alignment. When a tuple
1018-
of ints, one int per Dimension should be provided.
10191016
10201017
Examples
10211018
--------
@@ -1271,25 +1268,12 @@ def __halo_setup__(self, **kwargs):
12711268
return DimensionTuple(*halo, getters=self.dimensions)
12721269

12731270
def __padding_setup__(self, **kwargs):
1274-
padding = kwargs.get('padding')
1275-
if padding is None:
1276-
if self.is_autopaddable:
1277-
padding = self.__padding_setup_smart__(**kwargs)
1278-
else:
1279-
padding = super().__padding_setup__(**kwargs)
1280-
1281-
elif isinstance(padding, DimensionTuple):
1282-
padding = tuple(padding[d] for d in self.dimensions)
1283-
1284-
elif is_integer(padding):
1285-
padding = tuple((0, padding) if d.is_Space else (0, 0)
1286-
for d in self.dimensions)
1287-
1288-
elif isinstance(padding, tuple) and len(padding) == self.ndim:
1289-
padding = tuple((0, i) if is_integer(i) else i for i in padding)
1290-
1271+
# `padding=` is never honored: derived from policy to avoid stride
1272+
# inconsistencies between Functions sharing dimensions/halo
1273+
if self.is_autopaddable:
1274+
padding = self.__padding_setup_smart__(**kwargs)
12911275
else:
1292-
raise TypeError(f"`padding` must be int or {self.ndim}-tuple of ints")
1276+
return super().__padding_setup__(**kwargs)
12931277
return DimensionTuple(*padding, getters=self.dimensions)
12941278

12951279
@property
@@ -1410,9 +1394,6 @@ class TimeFunction(Function):
14101394
Controller for memory allocation. To be used, for example, when one wants
14111395
to take advantage of the memory hierarchy in a NUMA architecture. Refer to
14121396
`default_allocator.__doc__` for more information.
1413-
padding : int or tuple of ints, optional
1414-
Allocate extra grid points to maximize data access alignment. When a tuple
1415-
of ints, one int per Dimension should be provided.
14161397
14171398
Examples
14181399
--------

devito/types/misc.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,6 @@ def __init_finalize__(self, *args, shift=None, **kwargs):
267267
# for homogeneity reasons
268268
self._shift = as_tuple(shift)
269269

270-
def __padding_setup__(self, **kwargs):
271-
padding = kwargs.pop('padding', None)
272-
if padding is None:
273-
padding = self.__padding_setup_smart__(**kwargs)
274-
return super().__padding_setup__(padding=padding, **kwargs)
275-
276270
@property
277271
def shift(self):
278272
return self._shift

examples/userapi/01_dsl.ipynb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,6 @@
132132
" Controller for memory allocation. To be used, for example, when one wants\n",
133133
" to take advantage of the memory hierarchy in a NUMA architecture. Refer to\n",
134134
" `default_allocator.__doc__` for more information.\n",
135-
" padding : int or tuple of ints, optional\n",
136-
" Allocate extra grid points to maximize data access alignment. When a tuple\n",
137-
" of ints, one int per Dimension should be provided.\n",
138135
"\n",
139136
" Examples\n",
140137
" --------\n",
@@ -700,7 +697,7 @@
700697
"name": "stderr",
701698
"output_type": "stream",
702699
"text": [
703-
"Operator `Kernel` ran in 0.07 s\n"
700+
"Operator `Kernel` ran in 0.06 s\n"
704701
]
705702
},
706703
{

tests/test_data.py

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ class TestMetaData:
325325

326326
def test_wo_halo_wo_padding(self):
327327
grid = Grid(shape=(4, 4, 4))
328-
u = Function(name='u', grid=grid, space_order=0, padding=0)
328+
u = Function(name='u', grid=grid, space_order=0)
329329

330330
assert u.shape == u._shape_with_inhalo == u.shape_allocated
331331
assert u.shape_with_halo == u._shape_with_inhalo # W/o MPI, these two coincide
@@ -336,7 +336,7 @@ def test_wo_halo_wo_padding(self):
336336

337337
def test_w_halo_wo_padding(self):
338338
grid = Grid(shape=(4, 4, 4))
339-
u = Function(name='u', grid=grid, space_order=2, padding=0)
339+
u = Function(name='u', grid=grid, space_order=2)
340340

341341
assert len(u.shape) == len(u._size_halo.left)
342342
assert u._size_halo == u._size_owned == ((2, 2), (2, 2), (2, 2))
@@ -349,7 +349,7 @@ def test_w_halo_wo_padding(self):
349349

350350
# Try with different grid shape and space_order
351351
grid2 = Grid(shape=(3, 3, 3))
352-
u2 = Function(name='u2', grid=grid2, space_order=4, padding=0)
352+
u2 = Function(name='u2', grid=grid2, space_order=4)
353353
assert u2.shape == (3, 3, 3)
354354
assert u2._offset_domain == (4, 4, 4)
355355
assert u2._offset_halo == ((0, 7), (0, 7), (0, 7))
@@ -358,57 +358,25 @@ def test_w_halo_wo_padding(self):
358358
) == u2.shape_with_halo
359359
assert u2.shape_with_halo == (11, 11, 11)
360360

361-
def test_wo_halo_w_padding(self):
362-
grid = Grid(shape=(4, 4, 4))
363-
u = Function(name='u', grid=grid, space_order=2, padding=((1, 1), (3, 3), (4, 4)))
364-
365-
assert tuple(
366-
i + j + k for i, (j, k) in zip(u.shape_with_halo, u._padding, strict=True)
367-
) == u.shape_allocated
368-
assert u._halo == ((2, 2), (2, 2), (2, 2))
369-
assert u._size_padding == ((1, 1), (3, 3), (4, 4))
370-
assert u._size_padding.left == u._size_padding.right == (1, 3, 4)
371-
assert u._size_nodomain == ((3, 3), (5, 5), (6, 6))
372-
assert u._size_nodomain.left == u._size_nodomain.right == (3, 5, 6)
373-
assert u._size_nopad == (8, 8, 8)
374-
assert u._offset_domain == (3, 5, 6)
375-
assert u._offset_halo == ((1, 7), (3, 9), (4, 10))
376-
assert u._offset_halo.left == (1, 3, 4)
377-
assert u._offset_halo.right == (7, 9, 10)
378-
assert u._offset_owned == ((3, 5), (5, 7), (6, 8))
379-
380-
def test_w_halo_w_padding(self):
381-
grid = Grid(shape=(4, 4, 4))
382-
u = Function(name='u', grid=grid, space_order=(2, 1, 4),
383-
padding=((1, 1), (2, 2), (3, 3)))
384-
385-
assert u._size_halo == ((1, 4), (1, 4), (1, 4))
386-
assert u._size_owned == ((4, 1), (4, 1), (4, 1))
387-
assert u._size_nodomain == ((2, 5), (3, 6), (4, 7))
388-
assert u._size_nodomain.left == (2, 3, 4)
389-
assert u._size_nodomain.right == (5, 6, 7)
390-
assert u._size_nopad == (9, 9, 9)
391-
assert u._offset_domain == (2, 3, 4)
392-
assert u._offset_halo == ((1, 6), (2, 7), (3, 8))
393-
assert u._offset_owned == ((2, 5), (3, 6), (4, 7))
394-
395-
@switchconfig(autopadding=True, platform='bdw') # Platform is to fix pad value
361+
@switchconfig(autopadding=True, platform='bdw')
396362
def test_w_halo_w_autopadding(self):
397363
grid = Grid(shape=(4, 4, 4))
398364
u0 = Function(name='u0', grid=grid, space_order=0)
399365
u1 = Function(name='u1', grid=grid, space_order=3)
400366

401-
assert configuration['platform'].simd_items_per_reg(u1.dtype) == 8
367+
mmts = configuration['platform'].max_mem_trans_size(u1.dtype)
402368

369+
u0_pad = mmts - 4 # RoundUp(4, mmts) - 4
403370
assert u0._size_halo == ((0, 0), (0, 0), (0, 0))
404-
assert u0._size_padding == ((0, 0), (0, 0), (0, 12))
371+
assert u0._size_padding == ((0, 0), (0, 0), (0, u0_pad))
405372
assert u0._size_nodomain == u0._size_padding
406-
assert u0.shape_allocated == (4, 4, 16)
373+
assert u0.shape_allocated == (4, 4, 4 + u0_pad)
407374

375+
u1_pad = mmts - 10 # RoundUp(3+4+3, mmts) - (3+4+3)
408376
assert u1._size_halo == ((3, 3), (3, 3), (3, 3))
409-
assert u1._size_padding == ((0, 0), (0, 0), (0, 6)) # 6 stems from 16-(3+4+3)
410-
assert u1._size_nodomain == ((3, 3), (3, 3), (3, 9))
411-
assert u1.shape_allocated == (10, 10, 16)
377+
assert u1._size_padding == ((0, 0), (0, 0), (0, u1_pad))
378+
assert u1._size_nodomain == ((3, 3), (3, 3), (3, 3 + u1_pad))
379+
assert u1.shape_allocated == (10, 10, 10 + u1_pad)
412380

413381
@switchconfig(autopadding=True, platform='bdw')
414382
def test_temp_array_smart_padding_no_overshoot(self):

0 commit comments

Comments
 (0)