Skip to content

Commit f374bc2

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

5 files changed

Lines changed: 78 additions & 12 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: 46 additions & 11 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,19 @@ 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
615+
efuncs, init = make_zero_init(obj, self.rcompile, self.sregistry)
616+
617+
return (init,), efuncs
587618

588619
def _map_array_on_high_bw_mem(self, site, obj, storage):
589620
"""
@@ -704,16 +735,17 @@ def process(self, graph):
704735

705736
def make_zero_init(obj, rcompile, sregistry):
706737
cdims = []
707-
for d, (h0, h1), s in zip(
708-
obj.dimensions, obj._size_halo, obj.symbolic_shape, strict=True
738+
for d, (h0, h1), (p0, p1), s in zip(
739+
obj.dimensions, obj._size_halo, obj._size_padding, obj.symbolic_shape,
740+
strict=True
709741
):
710742
if d.is_NonlinearDerived:
711-
assert h0 == h1 == 0
743+
assert h0 == h1 == p0 == p1 == 0
712744
m = 0
713745
M = s - 1
714746
else:
715747
m = d.symbolic_min - h0
716-
M = d.symbolic_max + h1
748+
M = d.symbolic_max + h1 + (0 if is_integer(p1) else p1)
717749
cdims.append(CustomDimension(name=d.name, parent=d,
718750
symbolic_min=m, symbolic_max=M))
719751

@@ -722,7 +754,10 @@ def make_zero_init(obj, rcompile, sregistry):
722754
else:
723755
eqns = [Eq(obj[cdims], 0)]
724756

725-
irs, byproduct = rcompile(eqns)
757+
# No tiling: a zeroing kernel gains nothing from it, and nvc++ trips over
758+
# the padded loop bounds when it is asked to tile them
759+
irs, byproduct = rcompile(eqns, options={'zero-init': False,
760+
'par-tile': False})
726761

727762
init = irs.iet.body.body[0]
728763

‎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/types/basic.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,12 @@ def _size_nodomain(self):
13511351

13521352
return DimensionTuple(*sizes, getters=self.dimensions, left=left, right=right)
13531353

1354+
_is_zero_init = False
1355+
"""
1356+
Whether the entries outside `self`'s DOMAIN carry meaningful data rather
1357+
than scratch, in which case the whole allocation must be zeroed upfront.
1358+
"""
1359+
13541360
@property
13551361
def _is_reduction_ready(self):
13561362
"""

‎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)