Skip to content

Parallel Map Fusion #1987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

philip-paul-mueller
Copy link
Collaborator

@philip-paul-mueller philip-paul-mueller commented Apr 28, 2025

This PR extends the MapFusion transformation such that it also handles maps that are parallel.
The functionality that had to be added is actually quite small, as the transformation already implemented all functions that were needed.
The only thing that was needed is some dispatch logic.

While MapFusion will by default not perform parallel map fusion, for this a flag has to be specified, the FullMapFuse pass will do that.
However, in auto optimize this is disabled for compatibility.

This PR replaces PR#1965.

Initially this PR was a bugfix, but it kind of changed and become the real parallel Map fusion PR.

Copy link
Contributor

@alexnick83 alexnick83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a great idea to have the standalone capability to fuse parallel Maps rather than relying on SubgraphFusion. Relaxing the single-consumer constraint for the serial fusion's intermediate also makes sense to me.

My main question is whether an implementation of parallel fusion as a separate transformation has been considered, and what the trade-offs are. My understanding is that the fused implementation was chosen to better accommodate the FullMapFusion pass, but I wonder if the alternative would be more flexible. If this has been discussed and decided already internally, then I am fine. Otherwise, I would also ask for a review from someone currently actively working on transformations/auto-optimizer for a second opinion.

pass and `MapFusion`
pass and `MapFusion`.

By default this transformation only handles the case where to maps are right after each other,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
By default this transformation only handles the case where to maps are right after each other,
By default, this transformation only handles the case where two maps are executed in sequence,


By default this transformation only handles the case where to maps are right after each other,
separated by an intermediate array. However, by setting `allow_parallel_map_fusion` to `True`,
the transformation will be _in addition_ also be able to handle the case where the Maps are
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the transformation will be _in addition_ also be able to handle the case where the Maps are
the transformation will _in addition_ also be able to handle the case where the Maps are

By default this transformation only handles the case where to maps are right after each other,
separated by an intermediate array. However, by setting `allow_parallel_map_fusion` to `True`,
the transformation will be _in addition_ also be able to handle the case where the Maps are
parallel (parallel here means that neither of the two Map can be reached from the other; see
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parallel (parallel here means that neither of the two Map can be reached from the other; see
parallel (parallel here means that neither of the two Maps can be reached from the other; see


:param only_inner_maps: Only match Maps that are internal, i.e. inside another Map.
:param only_toplevel_maps: Only consider Maps that are at the top.
:param strict_dataflow: Which dataflow mode should be used, see above.
:param assume_always_shared: Assume that all intermediates are shared.
:param allow_serial_map_fusion: Allow serial map fusion, by default `True`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:param allow_serial_map_fusion: Allow serial map fusion, by default `True`.
:param allow_serial_map_fusion: Allow serial Map fusion, by default `True`.


:param only_inner_maps: Only match Maps that are internal, i.e. inside another Map.
:param only_toplevel_maps: Only consider Maps that are at the top.
:param strict_dataflow: Which dataflow mode should be used, see above.
:param assume_always_shared: Assume that all intermediates are shared.
:param allow_serial_map_fusion: Allow serial map fusion, by default `True`.
:param allow_parallel_map_fusion: Allow to merge parallel maps, by default `False`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:param allow_parallel_map_fusion: Allow to merge parallel maps, by default `False`.
:param allow_parallel_map_fusion: Allow to merge parallel Maps, by default `False`.

:param node1: The first node to check.
:param node2: The second node to check.
"""
# In order to be parallel they must be in the same scope.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rephrase this so there is no misunderstanding in the future. Nodes that are not in the same scope can definitely be parallel; you just do not want to try to merge Maps that are not in the same scope for semi-obvious reasons.

if scope[node1] != scope[node2]:
return False

# The `all_nodes_between()` function traverse the graph and returns `None` if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# The `all_nodes_between()` function traverse the graph and returns `None` if
# The `all_nodes_between()` function traverses the graph and returns `None` if

@@ -1273,6 +1529,7 @@ def has_read_write_dependency(
))
if not self.test_if_subsets_are_point_wise(all_subsets):
return True
del all_subsets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

@@ -1307,6 +1564,7 @@ def has_read_write_dependency(
# Now we can test if these subsets are point wise
if not self.test_if_subsets_are_point_wise(all_subsets):
return True
del all_subsets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

@philip-paul-mueller
Copy link
Collaborator Author

The parallel fusion of Maps (currently an open PR) is from an implementation point of view, a subset of the functionality you need to handle serial Map fusion.
Thus to avoid code duplication, you will end up with some kind of base class containing the common aspects.

In the very first version of the PR that introduced the new version of MapFusion there was this distinction between parallel and serial MapFusion.
However, it was decided that the parallel aspects should be removed to make the PR reviewable, or make it more managable.
Therefore no decision was made if they should be split to not.

This "subset aspect of the implementation" makes a split implementation very strange, you have a base, which essentially contains everything that is needed to implement relocate_node().
Then you have a class that implements parallel Map fusion, which essentially alias relocate_node() to apply() the only new thing it essentially adds is is_parallel().
And the idea to derive the serial Map fusion from the parallel version is just awkward.

However, my issue with splitting the implementation is, that I think there is no reason why a class that is called MapFusion should be unable to handle parallel Maps (okay the current default is to reject such cases).
If we split the implementation, then we should remove the name MapFusion completely and only retain SerialMapFusion and ParallelMapFusion (or variants thereof).

But I think we should discuss this aspect at the next DaCe meeting.

PS: I will not apply your correction yet, because I have multiple PRs about that subject open, so it get's complicated.

@philip-paul-mueller philip-paul-mueller changed the title MapFusion Multi Producer Intermediate Parallel Map Fusion Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants