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
1313from 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