Skip to content

Commit 8334fde

Browse files
committed
compiler: Zero an Array whose out-of-DOMAIN entries are data
1 parent e9db68d commit 8334fde

7 files changed

Lines changed: 84 additions & 13 deletions

File tree

‎devito/core/gpu.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,10 @@ def wrapper(expressions, mode='default', options=None, **kwargs1):
162162
# small kernels typically generated by recursive compilation
163163
par_tile0 = options0['par-tile']
164164
par_tile = options.get('par-tile')
165-
if par_tile0 and par_tile:
165+
if par_tile is False:
166+
# The caller explicitly opted out of tiling
167+
options = {**options0, **options, 'par-tile': ParTile(None)}
168+
elif par_tile0 and par_tile:
166169
options = {**options0, **options, 'par-tile': par_tile}
167170
elif par_tile0:
168171
par_tile = ParTile(par_tile0.default, default=par_tile0.default)

‎devito/passes/iet/definitions.py‎

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
VOID, Byref, DefFunction, FieldFromPointer, IndexedPointer, ListInitializer, SizeOf,
2121
as_long, pow_to_mul, unevaluate
2222
)
23-
from devito.tools import as_list, as_mapper, as_tuple, filter_sorted, flatten
23+
from devito.tools import as_list, as_mapper, as_tuple, filter_sorted, flatten, is_integer
2424
from devito.types import (
2525
Array, ComponentAccess, CustomDimension, DeviceMap, DeviceRM, Dimension, Eq, Symbol,
2626
size_t
@@ -91,6 +91,27 @@ def __init__(self, rcompile=None, sregistry=None, platform=None,
9191
self.sregistry = sregistry
9292
self.platform = platform
9393

94+
# Off inside the recursive compilation of a zero-init itself, which
95+
# would otherwise ask for a zero-init of its own, ad infinitum
96+
self.zero_init = (options or {}).get('zero-init', True)
97+
98+
def _zero_init(self, obj, storage):
99+
"""
100+
The nodes zeroing `obj` upfront, if it asks for it, plus the efuncs
101+
they call, if any.
102+
"""
103+
if not (obj._is_zero_init and self.zero_init):
104+
return (), ()
105+
106+
return self._make_zero_init(obj, storage)
107+
108+
def _make_zero_init(self, obj, storage):
109+
"""How to zero `obj`'s whole allocation, padding included."""
110+
storage.include(self.langbb['header-memcpy'])
111+
nbytes = SizeOf(obj._C_typedata)*as_long(obj.size)
112+
113+
return (self.langbb['host-memset'](obj._C_symbol, 0, nbytes),), ()
114+
94115
def _alloc_object_on_low_lat_mem(self, site, obj, storage):
95116
"""
96117
Allocate a LocalObject in the low latency memory.
@@ -172,11 +193,13 @@ def _alloc_host_array_on_high_bw_mem(self, site, obj, storage, *args):
172193
memptr = VOID(Byref(obj._C_symbol), '**')
173194
alignment = obj._data_alignment
174195
nbytes = SizeOf(obj._C_typedata)*as_long(obj.size)
175-
alloc = self.langbb['host-alloc'](memptr, alignment, nbytes)
196+
zeroing, efuncs = self._zero_init(obj, storage)
197+
allocs = [decl, self.langbb['host-alloc'](memptr, alignment, nbytes),
198+
*zeroing]
176199

177200
free = self.langbb['host-free'](obj._C_symbol)
178201

179-
storage.update(obj, site, allocs=(decl, alloc), frees=free)
202+
storage.update(obj, site, allocs=tuple(allocs), frees=free, efuncs=efuncs)
180203

181204
def _alloc_local_array_on_high_bw_mem(self, site, obj, storage, *args):
182205
"""
@@ -568,7 +591,7 @@ def __init__(self, options=None, **kwargs):
568591
self.gpu_create = options['gpu-create']
569592
self.gpu_place_transfers = options.get('place-transfers')
570593

571-
super().__init__(**kwargs)
594+
super().__init__(options=options, **kwargs)
572595

573596
def _alloc_local_array_on_high_bw_mem(self, site, obj, storage):
574597
"""
@@ -579,11 +602,22 @@ def _alloc_local_array_on_high_bw_mem(self, site, obj, storage):
579602
dofree = self.langbb['device-free']
580603

581604
nbytes = SizeOf(obj._C_typedata)*obj.size
582-
init = doalloc(nbytes, deviceid, retobj=obj)
605+
606+
zeroing, efuncs = self._zero_init(obj, storage)
607+
allocs = [doalloc(nbytes, deviceid, retobj=obj), *zeroing]
583608

584609
free = dofree(obj._C_name, deviceid)
585610

586-
storage.update(obj, site, allocs=init, frees=free)
611+
storage.update(obj, site, allocs=tuple(allocs), frees=free, efuncs=efuncs)
612+
613+
def _make_zero_init(self, obj, storage):
614+
# No language here has a device-side memset, so use a kernel. It gains
615+
# nothing from tiling, and nvc++ trips over the padded loop bounds
616+
# when it is asked to tile them
617+
efuncs, init = make_zero_init(obj, self.rcompile, self.sregistry,
618+
options={'par-tile': False})
619+
620+
return (init,), efuncs
587621

588622
def _map_array_on_high_bw_mem(self, site, obj, storage):
589623
"""
@@ -702,18 +736,20 @@ def process(self, graph):
702736
self.place_casts(graph)
703737

704738

705-
def make_zero_init(obj, rcompile, sregistry):
739+
def make_zero_init(obj, rcompile, sregistry, options=None):
706740
cdims = []
707-
for d, (h0, h1), s in zip(
708-
obj.dimensions, obj._size_halo, obj.symbolic_shape, strict=True
741+
for d, (h0, h1), (_, p1), s in zip(
742+
obj.dimensions, obj._size_halo, obj._size_padding, obj.symbolic_shape,
743+
strict=True
709744
):
710745
if d.is_NonlinearDerived:
711-
assert h0 == h1 == 0
746+
assert h0 == h1
712747
m = 0
713748
M = s - 1
714749
else:
715750
m = d.symbolic_min - h0
716-
M = d.symbolic_max + h1
751+
# Object needing padding zeroing need symbolic padding
752+
M = d.symbolic_max + h1 + (0 if is_integer(p1) else p1)
717753
cdims.append(CustomDimension(name=d.name, parent=d,
718754
symbolic_min=m, symbolic_max=M))
719755

@@ -722,7 +758,8 @@ def make_zero_init(obj, rcompile, sregistry):
722758
else:
723759
eqns = [Eq(obj[cdims], 0)]
724760

725-
irs, byproduct = rcompile(eqns)
761+
irs, byproduct = rcompile(eqns, options={'zero-init': False,
762+
**(options or {})})
726763

727764
init = irs.iet.body.body[0]
728765

‎devito/passes/iet/languages/C.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class CBB(LangBB):
5656
Call('free', (i,)),
5757
'host-free-pin': lambda i:
5858
Call('free', (i,)),
59+
'host-memset': lambda i, j, k:
60+
Call('memset', (i, j, k)),
5961
'alloc-global-symbol': lambda i, j, k:
6062
Call('memcpy', (i, j, k))
6163
}

‎devito/passes/iet/languages/CXX.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ class CXXBB(LangBB):
141141
Call('free', (i,)),
142142
'host-free-pin': lambda i:
143143
Call('free', (i,)),
144+
'host-memset': lambda i, j, k:
145+
Call('memset', (i, j, k)),
144146
'alloc-global-symbol': lambda i, j, k:
145147
Call('memcpy', (i, j, k))
146148
}

‎devito/types/basic.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,12 @@ class AbstractFunction(sympy.Function, Basic, Pickable, Evaluable):
714714
effect if autopadding is disabled, which is the default behavior.
715715
"""
716716

717+
_is_zero_init = False
718+
"""
719+
Whether the entries outside `self`'s DOMAIN carry meaningful data rather
720+
than scratch, in which case the whole allocation must be zeroed upfront.
721+
"""
722+
717723
__rkwargs__ = ('name', 'dtype', 'grid', 'halo', 'ghost',
718724
'alias', 'space', 'function', 'is_transient', 'avg_mode')
719725

‎devito/types/misc.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ class TempArray(Array):
257257
"""
258258

259259
is_autopaddable = True
260+
_is_zero_init = True
260261

261262
__rkwargs__ = (Array.__rkwargs__ + ('shift',))
262263

‎tests/test_operator.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,26 @@ def test_conditional_declarations(self):
13941394
assert i[0].is_Expression
13951395
assert i[0].expr.rhs is init_value
13961396

1397+
def test_zero_init_array(self):
1398+
"""
1399+
An Array whose entries outside the DOMAIN are data, rather than
1400+
scratch, is zeroed right after being allocated.
1401+
"""
1402+
grid = Grid(shape=(4, 4))
1403+
1404+
class ZeroInitArray(Array):
1405+
_is_zero_init = True
1406+
1407+
a = ZeroInitArray(name='a', dimensions=grid.dimensions,
1408+
dtype=grid.dtype, space='local')
1409+
b = Array(name='b', dimensions=grid.dimensions, dtype=grid.dtype,
1410+
space='local')
1411+
1412+
f = Function(name='f', grid=grid)
1413+
1414+
assert 'memset(a' in str(Operator(Eq(f, a.indexify())))
1415+
assert 'memset(b' not in str(Operator(Eq(f, b.indexify())))
1416+
13971417
def test_nested_scalar_assigns(self):
13981418
grid = Grid(shape=(4, 4))
13991419

0 commit comments

Comments
 (0)