Skip to content

Commit 30041af

Browse files
committed
compiler: Improve task collection
1 parent 741e1b1 commit 30041af

1 file changed

Lines changed: 73 additions & 26 deletions

File tree

devito/passes/iet/orchestration.py

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
AsyncCall, AsyncCallable, BlankLine, Block, BusyWait, Call, Callable, Conditional,
1010
DummyExpr, List, SyncSpot, Transformer, derive_parameters, make_callable
1111
)
12-
from devito.ir.iet.visitors import LazyVisitor
12+
from devito.ir.iet.visitors import Visitor
1313
from devito.ir.support import (
1414
InitArray, PrefetchUpdate, ReleaseLock, SnapIn, SnapOut, SyncArray, WaitLock, WithLock
1515
)
@@ -123,7 +123,6 @@ def _make_prefetchupdate(self, iet, sync_ops, layer, wrap=True):
123123

124124
@iet_pass
125125
def process(self, iet):
126-
# The SyncOps are to be processed in a given order
127126
callbacks = {
128127
WaitLock: self._make_waitlock,
129128
WithLock: self._make_withlock,
@@ -136,8 +135,10 @@ def process(self, iet):
136135
AsyncCallable: self._make_async_callable
137136
}
138137

139-
# Collect the task groups to lower atomically, if any
140-
task_groups = CollectTasks().visit(iet) if self.npthreads else ()
138+
# Collect the compatible asynchronous task groups, if any
139+
task_groups = TaskGroups()
140+
if self.npthreads:
141+
CollectTasks(task_groups).visit(iet)
141142

142143
# Lower the SyncSpots in a single bottom-up traversal, atomically lowering
143144
lowerer = LowerSyncSpots(callbacks, task_groups)
@@ -146,27 +147,75 @@ def process(self, iet):
146147
return iet, {'efuncs': lowerer.efuncs}
147148

148149

149-
class CollectTasks(LazyVisitor):
150+
class TaskGroups:
151+
152+
"""
153+
A collection of compatible asynchronous task groups.
154+
155+
Tasks are grouped by their enclosing iteration, group ID, synchronization
156+
operation type, and snapshot scope. A group is activated after the last
157+
anchor registered for it.
158+
159+
Examples
160+
--------
161+
Two `WithLock` tasks with the same grouping metadata are collected into one
162+
group, activated after `anchor1`::
163+
164+
groups = TaskGroups()
165+
groups.add(task0, anchor0, iteration, gid, WithLock, False)
166+
groups.add(task1, anchor1, iteration, gid, WithLock, False)
167+
168+
groups.sync_spots == {task0.spot, task1.spot}
169+
groups.by_anchor[anchor1] == [([task0, task1], WithLock)]
170+
"""
171+
172+
def __init__(self):
173+
self._groups = defaultdict(list)
174+
self._anchors = {}
175+
self._sync_spots = set()
176+
177+
def add(self, task, anchor, iteration, gid, optype, in_snapshot):
178+
key = TaskGroupKey(iteration, gid, optype, in_snapshot)
179+
self._groups[key].append(task)
180+
self._anchors[key] = anchor
181+
self._sync_spots.add(task.spot)
182+
183+
@property
184+
def sync_spots(self):
185+
return self._sync_spots
186+
187+
@property
188+
def by_anchor(self):
189+
mapper = defaultdict(list)
190+
for key, tasks in self._groups.items():
191+
mapper[self._anchors[key]].append((tasks, key.optype))
192+
return mapper
193+
194+
195+
class CollectTasks(Visitor):
150196

151197
"""Collect and group compatible asynchronous SyncSpots."""
152198

153-
def _post_visit(self, ret):
154-
groups = defaultdict(list)
155-
anchors = {}
156-
for key, task, anchor in ret:
157-
groups[key].append(task)
158-
# Activate the group after its last task in program order
159-
anchors[key] = anchor
160-
return tuple((tasks, anchors[key], key.optype)
161-
for key, tasks in groups.items())
199+
def __init__(self, task_groups):
200+
super().__init__()
201+
self._task_groups = task_groups
202+
203+
def visit_object(self, o, **kwargs):
204+
pass
205+
206+
def visit_tuple(self, o, **kwargs):
207+
for i in o:
208+
self._visit(i, **kwargs)
209+
210+
visit_list = visit_tuple
162211

163212
def visit_Iteration(self, o, **kwargs):
164213
kwargs['iteration'] = o
165-
yield from self._visit(o.children, **kwargs)
214+
self._visit(o.children, **kwargs)
166215

167216
def visit_Conditional(self, o, **kwargs):
168217
kwargs['condition'] = o
169-
yield from self._visit(o.children, **kwargs)
218+
self._visit(o.children, **kwargs)
170219

171220
def visit_SyncSpot(self, o, iteration=None, condition=None,
172221
in_snapshot=False):
@@ -190,16 +239,17 @@ def visit_SyncSpot(self, o, iteration=None, condition=None,
190239
gid, = {i.gid for i in sync_ops}
191240
if gid is not None:
192241
task = Task(o, guard, sync_ops)
193-
key = TaskGroupKey(iteration, gid, optype, in_snapshot)
194-
yield key, task, condition or o
242+
self._task_groups.add(
243+
task, condition or o, iteration, gid, optype, in_snapshot
244+
)
195245

196246
break
197247

198248
if any(isinstance(i, SnapOut) for i in o.sync_ops):
199249
# Do not mix composite tasks with other compatible groups
200250
in_snapshot = True
201251

202-
yield from self._visit(
252+
self._visit(
203253
o.children, iteration=iteration, condition=condition,
204254
in_snapshot=in_snapshot
205255
)
@@ -218,7 +268,7 @@ class LowerSyncSpots(Transformer):
218268
----------
219269
callbacks : mapping
220270
The callbacks used to lower `SyncOp`s and create asynchronous callables.
221-
task_groups : iterable
271+
task_groups : TaskGroups
222272
The task groups to lower atomically.
223273
"""
224274

@@ -227,11 +277,8 @@ def __init__(self, callbacks, task_groups):
227277

228278
self._callbacks = callbacks
229279

230-
self._task_bodies = {}
231-
self._anchors = defaultdict(list)
232-
for tasks, anchor, optype in task_groups:
233-
self._task_bodies.update((task.spot, None) for task in tasks)
234-
self._anchors[anchor].append((tasks, optype))
280+
self._task_bodies = dict.fromkeys(task_groups.sync_spots)
281+
self._task_groups = task_groups.by_anchor
235282

236283
self._efuncs = []
237284

@@ -289,7 +336,7 @@ def _lower_task_groups(self, o, iet):
289336
The call is inserted after `o`; each task retains its own guard.
290337
"""
291338
calls = []
292-
for tasks, optype in self._anchors.get(o, ()):
339+
for tasks, optype in self._task_groups.get(o, ()):
293340
sync_ops = tuple(op for task in tasks for op in task.sync_ops)
294341
layer = infer_sync_layer(sync_ops)
295342

0 commit comments

Comments
 (0)