Skip to content

Commit 609aa97

Browse files
committed
edit
1 parent e60bd28 commit 609aa97

3 files changed

Lines changed: 54 additions & 26 deletions

File tree

src/gt4py/next/program_processors/runners/dace/lowering/gtir_dataflow.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def connect(
269269
map_exit: Optional[dace_nodes.MapExit],
270270
dest: dace_nodes.AccessNode,
271271
dest_subset: dace_subsets.Range,
272+
allow_removal_of_last_node: bool,
272273
) -> bool:
273274
"""Create a connection to the `dest` node, writing the given `dest_subset`.
274275
@@ -277,35 +278,34 @@ def connect(
277278
outside data container is removed, the caller is responsible to propagate
278279
the strides of the destination array to the array inside the nested SDFG.
279280
"""
280-
# We don't allow removing the last node of the dataflow inside a map scope,
281-
# because the same reults could be written to multiple destiation nodes ouside
282-
# the map scope, in case of field operators returning tuples.
283-
allow_removal_of_last_node: Final[bool] = False
284281
dest_desc = self.result.dc_node.desc(self.state)
285282
write_edge = self.state.in_edges(self.result.dc_node)[0]
286283

287-
if self.state.out_degree(self.result.dc_node) != 0:
288-
remove_last_node = False
289-
elif isinstance(write_edge.src, dace_nodes.Tasklet):
290-
# The temporary data written by a tasklet can be safely deleted.
291-
remove_last_node = True
292-
elif isinstance(write_edge.src, dace_nodes.NestedSDFG):
293-
if isinstance(dest_desc, dace.data.Scalar):
294-
# We keep scalar temporary storage, as a general rule, since it
295-
# does not affect performance of the generated code. This scalar
296-
# is only required in some cases, e.g. for nested SDFGs implementing
297-
# reduction, which use a WCR memlet for the reduction operation.
284+
if allow_removal_of_last_node:
285+
if self.state.out_degree(self.result.dc_node) != 0:
298286
remove_last_node = False
299-
else:
300-
# We remove the transient array on the output connection of a nested
301-
# SDFG and write directly to the destination node.
302-
# The caller is responsible to propagate the strides of the destination
303-
# array to the array inside the nested SDFG.
287+
elif isinstance(write_edge.src, dace_nodes.Tasklet):
288+
# The temporary data written by a tasklet can be safely deleted.
304289
remove_last_node = True
290+
elif isinstance(write_edge.src, dace_nodes.NestedSDFG):
291+
if isinstance(dest_desc, dace.data.Scalar):
292+
# We keep scalar temporary storage, as a general rule, since it
293+
# does not affect performance of the generated code. This scalar
294+
# is only required in some cases, e.g. for nested SDFGs implementing
295+
# reduction, which use a WCR memlet for the reduction operation.
296+
remove_last_node = False
297+
else:
298+
# We remove the transient array on the output connection of a nested
299+
# SDFG and write directly to the destination node.
300+
# The caller is responsible to propagate the strides of the destination
301+
# array to the array inside the nested SDFG.
302+
remove_last_node = True
303+
else:
304+
remove_last_node = False
305305
else:
306306
remove_last_node = False
307307

308-
if allow_removal_of_last_node and remove_last_node:
308+
if remove_last_node:
309309
src_node = write_edge.src
310310
src_node_connector = write_edge.src_conn
311311
src_subset = write_edge.data.src_subset

src/gt4py/next/program_processors/runners/dace/lowering/gtir_to_sdfg_primitives.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from __future__ import annotations
1010

1111
import abc
12+
from collections import Counter
1213
from typing import TYPE_CHECKING, Iterable, Optional, Protocol
1314

1415
import dace
@@ -95,6 +96,7 @@ def _create_field_operator_impl(
9596
output_edge: gtir_dataflow.DataflowOutputEdge,
9697
output_type: ts.FieldType,
9798
map_exit: dace_nodes.MapExit,
99+
output_consumer_count: dict[dace_nodes.AccessNode, int],
98100
) -> gtir_to_sdfg_types.FieldopData:
99101
"""
100102
Helper method to allocate a temporary array that stores one field computed
@@ -166,8 +168,11 @@ def _create_field_operator_impl(
166168
)
167169
field_node = ctx.state.add_access(field_name)
168170

169-
# and here the edge writing the dataflow result data through the map exit node
170-
output_edge.connect(map_exit, field_node, field_subset)
171+
# and here the edge writing the dataflow result data through the map exit node.
172+
# Note that we cannot remove the output data access node only if this is used
173+
# for mutiple fields in a return tuple.
174+
allow_removal_of_last_node = output_consumer_count[output_edge.result.dc_node] == 1
175+
output_edge.connect(map_exit, field_node, field_subset, allow_removal_of_last_node)
171176

172177
return gtir_to_sdfg_types.FieldopData(
173178
field_node, ts.FieldType(field_dims, output_edge.result.gt_dtype), tuple(field_origin)
@@ -217,17 +222,24 @@ def _create_field_operator(
217222
for edge in input_edges:
218223
edge.connect(map_entry)
219224

225+
# The same output node could be used for multiple fields in case of tuple return.
226+
# In this case, the output access node cannot be removed.
227+
consumer_count = Counter(
228+
oedge.result.dc_node
229+
for oedge in gtx_utils.flatten_nested_tuple((output_tree,))
230+
if oedge is not None
231+
)
220232
if isinstance(node_type, ts.FieldType):
221233
assert isinstance(output_tree, gtir_dataflow.DataflowOutputEdge)
222234
return _create_field_operator_impl(
223-
ctx, sdfg_builder, domain, output_tree, node_type, map_exit
235+
ctx, sdfg_builder, domain, output_tree, node_type, map_exit, consumer_count
224236
)
225237
else:
226238
# handle tuples of fields
227239
output_symbol_tree = gtir_to_sdfg_utils.make_symbol_tree("x", node_type)
228240
return gtx_utils.tree_map(
229241
lambda output_edge, output_sym: _create_field_operator_impl(
230-
ctx, sdfg_builder, domain, output_edge, output_sym.type, map_exit
242+
ctx, sdfg_builder, domain, output_edge, output_sym.type, map_exit, consumer_count
231243
)
232244
)(output_tree, output_symbol_tree)
233245

src/gt4py/next/program_processors/runners/dace/lowering/gtir_to_sdfg_scan.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from __future__ import annotations
2424

25+
from collections import Counter
2526
from typing import Iterable, Sequence
2627

2728
import dace
@@ -73,6 +74,7 @@ def _create_scan_field_operator_impl(
7374
output_domain: infer_domain.NonTupleDomainAccess,
7475
output_type: ts.FieldType,
7576
map_exit: dace_nodes.MapExit | None,
77+
output_consumer_count: dict[dace_nodes.AccessNode, int],
7678
) -> gtir_to_sdfg_types.FieldopData | None:
7779
"""
7880
Helper method to allocate a temporary array that stores one field computed
@@ -146,7 +148,12 @@ def _create_scan_field_operator_impl(
146148
# Up to now the nested SDFG is writing into a transient data container that
147149
# has the size to hold one column. The function below, that does the connection,
148150
# will remove that transient and write directly to the result field.
149-
inner_map_output_temporary_removed = output_edge.connect(map_exit, field_node, field_subset)
151+
# Note that we cannot remove the output data access node only if this is used
152+
# for mutiple fields in a return tuple.
153+
allow_removal_of_last_node = output_consumer_count[output_edge.result.dc_node] == 1
154+
inner_map_output_temporary_removed = output_edge.connect(
155+
map_exit, field_node, field_subset, allow_removal_of_last_node
156+
)
150157
if not inner_map_output_temporary_removed:
151158
raise ValueError("The scan nested SDFG is expected to write directly to the result field.")
152159

@@ -244,6 +251,14 @@ def _create_scan_field_operator(
244251
else im.sym("__gtir_unused_dummy_var", node_type)
245252
)
246253

254+
# The same output node could be used for multiple fields in case of tuple return.
255+
# In this case, the output access node cannot be removed.
256+
consumer_count = Counter(
257+
oedge.result.dc_node
258+
for oedge in gtx_utils.flatten_nested_tuple((output,))
259+
if oedge is not None
260+
)
261+
247262
return gtx_utils.tree_map(
248263
lambda edge, domain, sym: _create_scan_field_operator_impl(
249264
ctx,
@@ -252,6 +267,7 @@ def _create_scan_field_operator(
252267
domain,
253268
sym.type,
254269
map_exit,
270+
consumer_count,
255271
)
256272
)(output, output_domain, dummy_output_symbol)
257273

0 commit comments

Comments
 (0)